Skip to content

Commit 46eb182

Browse files
feat(plotly): implement barcode-code128 (#7571)
## Implementation: `barcode-code128` - python/plotly Implements the **python/plotly** version of `barcode-code128`. **File:** `plots/barcode-code128/implementations/python/plotly.py` **Parent Issue:** #3809 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26198312152)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent f199080 commit 46eb182

2 files changed

Lines changed: 330 additions & 152 deletions

File tree

plots/barcode-code128/implementations/python/plotly.py

Lines changed: 155 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
barcode-code128: Code 128 Barcode
3-
Library: plotly 6.5.2 | Python 3.13.11
4-
Quality: 91/100 | Created: 2026-01-19
3+
Library: plotly 6.7.0 | Python 3.13.13
4+
Quality: 94/100 | Updated: 2026-05-21
55
"""
66

7+
import os
8+
79
import plotly.graph_objects as go
810

911

10-
# Code 128 encoding patterns (0 = space, 1 = bar)
11-
# Each character is 11 modules (6 bars, quiet zone not included)
12+
# Theme tokens
13+
THEME = os.getenv("ANYPLOT_THEME", "light")
14+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
15+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
16+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
17+
18+
# Code 128 encoding patterns (11-module per symbol; STOP is 13 modules)
1219
CODE128_PATTERNS = {
13-
# Start codes
1420
"START_A": "11010000100",
1521
"START_B": "11010010000",
1622
"START_C": "11010011100",
17-
"STOP": "1100011101011", # Stop pattern (13 modules)
18-
# Values 0-106
23+
"STOP": "1100011101011",
1924
0: "11011001100",
2025
1: "11001101100",
2126
2: "11001100110",
@@ -125,86 +130,190 @@
125130
106: "1100011101011",
126131
}
127132

128-
# Code 128B character to value mapping (ASCII 32-127)
133+
# Code 128B: ASCII 32-127 mapped to values 0-95
129134
CODE128B_MAP = {chr(i): i - 32 for i in range(32, 128)}
130135

131-
# Data - encode a sample string
132-
content = "PYPLOTS-2024"
136+
# Data — shipping tracking code (Code 128B subset covers full alphanumeric range)
137+
content = "SHIP-2024-ABC123"
133138

134-
# Encode using Code 128B
135-
values = [104] # Start B
139+
# Encode using Code 128B subset
140+
values = [104] # START_B
136141
for char in content:
137-
if char in CODE128B_MAP:
138-
values.append(CODE128B_MAP[char])
139-
else:
140-
values.append(0) # Space for unsupported chars
142+
values.append(CODE128B_MAP.get(char, 0))
141143

142-
# Calculate check digit (modulo 103)
144+
# Check digit: modulo 103 algorithm (mandatory per spec)
143145
checksum = values[0]
144146
for i, val in enumerate(values[1:], 1):
145147
checksum += i * val
146148
checksum = checksum % 103
147149
values.append(checksum)
148150

149-
# Build pattern
151+
# Build binary pattern: START_B + data symbols + check digit + STOP
150152
barcode_pattern = CODE128_PATTERNS["START_B"]
151153
for val in values[1:-1]:
152154
barcode_pattern += CODE128_PATTERNS[val]
153-
barcode_pattern += CODE128_PATTERNS[values[-1]] # Check digit
155+
barcode_pattern += CODE128_PATTERNS[values[-1]]
154156
barcode_pattern += CODE128_PATTERNS["STOP"]
155157

156-
# Calculate bar positions
157-
bar_width = 3
158+
# Calculate bar positions and widths from binary pattern
159+
QUIET_ZONE = 80 # generous quiet zone for reliable scanning
160+
MODULE_W = 4 # data units per module
158161
x_positions = []
159-
widths = []
160-
current_x = 50 # Start with quiet zone
162+
bar_widths_list = []
163+
current_x = QUIET_ZONE
161164

162165
for i, bit in enumerate(barcode_pattern):
163166
if bit == "1":
164-
# Find consecutive 1s
165167
if i == 0 or barcode_pattern[i - 1] == "0":
166-
start_x = current_x
167-
width = bar_width
168+
width = MODULE_W
168169
j = i + 1
169170
while j < len(barcode_pattern) and barcode_pattern[j] == "1":
170-
width += bar_width
171+
width += MODULE_W
171172
j += 1
172-
x_positions.append(start_x + width / 2)
173-
widths.append(width)
174-
current_x += bar_width
173+
x_positions.append(current_x + width / 2)
174+
bar_widths_list.append(width)
175+
current_x += MODULE_W
176+
177+
total_width = QUIET_ZONE + len(barcode_pattern) * MODULE_W + QUIET_ZONE
178+
179+
# Structural region boundaries in x coordinates (modules: START=11, each char=11, STOP=13)
180+
START_X0 = QUIET_ZONE
181+
START_X1 = QUIET_ZONE + 11 * MODULE_W
182+
183+
data_start_x = START_X1
184+
data_end_x = data_start_x + len(content) * 11 * MODULE_W
185+
186+
check_x0 = data_end_x
187+
check_x1 = check_x0 + 11 * MODULE_W
175188

176-
# Calculate total width
177-
total_width = 50 + len(barcode_pattern) * bar_width + 50 # quiet zones
189+
stop_x0 = check_x1
190+
stop_x1 = stop_x0 + 13 * MODULE_W
178191

179192
# Plot
180193
fig = go.Figure()
181194

182-
# Add bars
183-
for x, w in zip(x_positions, widths, strict=True):
184-
fig.add_shape(type="rect", x0=x - w / 2, x1=x + w / 2, y0=100, y1=500, fillcolor="black", line={"width": 0})
195+
BAR_Y0 = 90
196+
BAR_Y1 = 510
197+
mid_bar_y = (BAR_Y0 + BAR_Y1) / 2
198+
199+
# Barcode bars — tall to utilize canvas space
200+
for x, w in zip(x_positions, bar_widths_list, strict=True):
201+
fig.add_shape(type="rect", x0=x - w / 2, x1=x + w / 2, y0=BAR_Y0, y1=BAR_Y1, fillcolor=INK, line={"width": 0})
202+
203+
# --- Structural anatomy annotations ---
204+
# Horizontal bracket line spanning the full barcode (above bars)
205+
BRACKET_Y = BAR_Y1 + 14
206+
fig.add_shape(
207+
type="line", x0=START_X0, x1=stop_x1, y0=BRACKET_Y, y1=BRACKET_Y, line={"color": INK_SOFT, "width": 1}, opacity=0.55
208+
)
209+
210+
# Vertical tick marks at each region boundary
211+
for bx in [START_X0, START_X1, data_end_x, check_x1, stop_x1]:
212+
fig.add_shape(
213+
type="line",
214+
x0=bx,
215+
x1=bx,
216+
y0=BRACKET_Y - 6,
217+
y1=BRACKET_Y + 6,
218+
line={"color": INK_SOFT, "width": 1},
219+
opacity=0.55,
220+
)
221+
222+
# Region labels just above the bracket line (narrow regions use smaller font)
223+
LABEL_Y = BRACKET_Y + 10
224+
for x0, x1, label, fsize in [
225+
(START_X0, START_X1, "START B", 10),
226+
(data_start_x, data_end_x, f"DATA ({len(content)} chars)", 11),
227+
(check_x0, check_x1, "CHK", 10),
228+
(stop_x0, stop_x1, "STOP", 10),
229+
]:
230+
fig.add_annotation(
231+
x=(x0 + x1) / 2,
232+
y=LABEL_Y,
233+
text=label,
234+
showarrow=False,
235+
font={"size": fsize, "color": INK_SOFT},
236+
xanchor="center",
237+
yanchor="bottom",
238+
)
185239

186-
# Add human-readable text below barcode
240+
# --- Hover interactivity (distinctive plotly feature) ---
241+
# Per-character data hover points — reveals encoded value for each character
242+
char_xs = [data_start_x + (i * 11 + 5.5) * MODULE_W for i in range(len(content))]
243+
char_labels = [
244+
f"<b>'{char}'</b> · Code 128B value: {val}<br>Position {i + 1} / {len(content)}"
245+
for i, (char, val) in enumerate(zip(content, values[1:-1], strict=False))
246+
]
247+
fig.add_trace(
248+
go.Scatter(
249+
x=char_xs,
250+
y=[mid_bar_y] * len(char_xs),
251+
mode="markers",
252+
marker={"opacity": 0, "size": 30, "color": INK},
253+
text=char_labels,
254+
hovertemplate="%{text}<extra></extra>",
255+
showlegend=False,
256+
hoverlabel={"bgcolor": PAGE_BG, "bordercolor": INK_SOFT, "font": {"color": INK, "size": 13}},
257+
)
258+
)
259+
260+
# Structural region hover points
261+
for cx, hover_text in [
262+
((START_X0 + START_X1) / 2, "<b>START B Pattern</b><br>Signals Code 128 subset B (ASCII 32–127)<br>11 modules"),
263+
((check_x0 + check_x1) / 2, f"<b>Check Digit: {checksum}</b><br>Modulo 103 of weighted symbol sum<br>11 modules"),
264+
((stop_x0 + stop_x1) / 2, "<b>STOP Pattern</b><br>Terminates every Code 128 barcode<br>13 modules"),
265+
]:
266+
fig.add_trace(
267+
go.Scatter(
268+
x=[cx],
269+
y=[mid_bar_y],
270+
mode="markers",
271+
marker={"opacity": 0, "size": 20},
272+
hovertemplate=f"{hover_text}<extra></extra>",
273+
showlegend=False,
274+
hoverlabel={"bgcolor": PAGE_BG, "bordercolor": INK_SOFT, "font": {"color": INK, "size": 13}},
275+
)
276+
)
277+
278+
# Human-readable text below barcode
187279
fig.add_annotation(
188280
x=total_width / 2,
189-
y=50,
281+
y=45,
190282
text=content,
191283
showarrow=False,
192-
font={"size": 36, "family": "Courier New, monospace", "color": "black"},
284+
font={"size": 26, "family": "Courier New, monospace", "color": INK},
285+
xanchor="center",
286+
yanchor="middle",
287+
)
288+
289+
# Subset label above anatomy bracket
290+
fig.add_annotation(
291+
x=total_width / 2,
292+
y=600,
293+
text="Code 128B · ASCII Subset (32–127)",
294+
showarrow=False,
295+
font={"size": 18, "color": INK_SOFT},
193296
xanchor="center",
194297
yanchor="middle",
195298
)
196299

197300
# Layout
198301
fig.update_layout(
199-
title={"text": "barcode-code128 · plotly · pyplots.ai", "font": {"size": 28}, "x": 0.5, "xanchor": "center"},
302+
autosize=False,
303+
title={
304+
"text": "barcode-code128 · python · plotly · anyplot.ai",
305+
"font": {"size": 16, "color": INK},
306+
"x": 0.5,
307+
"xanchor": "center",
308+
},
200309
xaxis={"visible": False, "range": [0, total_width], "fixedrange": True},
201-
yaxis={"visible": False, "range": [0, 600], "fixedrange": True, "scaleanchor": "x", "scaleratio": 1},
202-
plot_bgcolor="white",
203-
paper_bgcolor="white",
204-
margin={"l": 50, "r": 50, "t": 100, "b": 50},
310+
yaxis={"visible": False, "range": [0, 650], "fixedrange": True},
311+
plot_bgcolor=PAGE_BG,
312+
paper_bgcolor=PAGE_BG,
313+
margin={"l": 80, "r": 40, "t": 80, "b": 60},
205314
showlegend=False,
206315
)
207316

208317
# Save
209-
fig.write_image("plot.png", width=1600, height=900, scale=3)
210-
fig.write_html("plot.html", include_plotlyjs="cdn")
318+
fig.write_image(f"plot-{THEME}.png", width=800, height=450, scale=4)
319+
fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn")

0 commit comments

Comments
 (0)