{"spec_id":"barcode-ean13","library":"makie","language":"julia","code":"# anyplot.ai\n# barcode-ean13: EAN-13 Barcode\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 90/100 | Created: 2026-09-01\n\nusing CairoMakie\nusing Colors\n\n# --- Theme tokens -----------------------------------------------------------\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\n\n# A real EAN-13 needs a light quiet zone to stay scannable, so the label\n# stock and bar ink are fixed rather than theme-adaptive — like a printed\n# barcode photographed on a page, this \"data\" doesn't flip with the theme.\nconst LABEL_BG   = colorant\"#FBF9F2\"\nconst BAR_INK    = colorant\"#15130F\"\nconst PAPER_TEXT = colorant\"#4A4A44\"  # fixed muted ink for text on the paper card\n\n# --- Data: encode a 13-digit EAN-13 -----------------------------------------\n# GS1 prefix \"400\" identifies Germany; the remaining digits are a\n# manufacturer reference, product reference, and check digit.\ncode   = \"4006381333931\"\ndigits = [parse(Int, c) for c in code]\n\n# Recompute the check digit from the first 12 digits (odd positions x1,\n# even positions x3, mod 10) to confirm the example is a valid EAN-13.\nodd_sum    = sum(digits[i] for i in 1:2:11)\neven_sum   = sum(digits[i] for i in 2:2:12)\ndigits[13] = (10 - (odd_sum + 3 * even_sum) % 10) % 10\n\n# 7-module digit patterns: L (left, odd parity), G (left, even parity), R (right)\nL_CODE = [\"0001101\", \"0011001\", \"0010011\", \"0111101\", \"0100011\",\n          \"0110001\", \"0101111\", \"0111011\", \"0110111\", \"0001011\"]\nG_CODE = [\"0100111\", \"0110011\", \"0011011\", \"0100001\", \"0011101\",\n          \"0111001\", \"0000101\", \"0010001\", \"0001001\", \"0010111\"]\nR_CODE = [\"1110010\", \"1100110\", \"1101100\", \"1000010\", \"1011100\",\n          \"1001110\", \"1010000\", \"1000100\", \"1001000\", \"1110100\"]\n\n# Which of the 6 left digits use L vs. G code, keyed by the leading digit\nPARITY = Dict(\n    0 => \"LLLLLL\", 1 => \"LLGLGG\", 2 => \"LLGGLG\", 3 => \"LLGGGL\", 4 => \"LGLLGG\",\n    5 => \"LGGLLG\", 6 => \"LGGGLL\", 7 => \"LGLGLG\", 8 => \"LGLGGL\", 9 => \"LGGLGL\",\n)\n\nfirst_digit    = digits[1]\nleft_digits    = digits[2:7]\nright_digits   = digits[8:13]\nparity         = PARITY[first_digit]\nleft_patterns  = [parity[i] == 'L' ? L_CODE[left_digits[i] + 1] : G_CODE[left_digits[i] + 1]\n                  for i in 1:6]\nright_patterns = [R_CODE[right_digits[i] + 1] for i in 1:6]\n\nSTART_GUARD, CENTER_GUARD, END_GUARD = \"101\", \"01010\", \"101\"\n\n# --- Geometry: lay out modules left to right --------------------------------\nquiet   = 9      # quiet-zone module widths (spec minimum on each side)\nbar_h   = 70.0   # data-bar height, in module-width units\nguard_h = 75.0   # guard bars run 5 modules deeper than data bars (GS1 spec)\n\nbars = Tuple{Float64,Float64}[]  # (x, height) of every printed (black) module\nx = Float64(quiet)\nfor bit in START_GUARD\n    bit == '1' && push!(bars, (x, guard_h))\n    global x += 1\nend\nfor pattern in left_patterns\n    for bit in pattern\n        bit == '1' && push!(bars, (x, bar_h))\n        global x += 1\n    end\nend\nfor bit in CENTER_GUARD\n    bit == '1' && push!(bars, (x, guard_h))\n    global x += 1\nend\nfor pattern in right_patterns\n    for bit in pattern\n        bit == '1' && push!(bars, (x, bar_h))\n        global x += 1\n    end\nend\nfor bit in END_GUARD\n    bit == '1' && push!(bars, (x, guard_h))\n    global x += 1\nend\ntotal_width = x + quiet\n\nleft_start  = quiet + 3\nright_start = quiet + 3 + 42 + 5\n\n# --- Plot ---------------------------------------------------------------\ntitle_str  = \"barcode-ean13 · julia · makie · anyplot.ai\"\ntitle_size = length(title_str) > 67 ? round(Int, 20 * 67 / length(title_str)) : 20\n\nfig = Figure(size = (1600, 900), fontsize = 14, backgroundcolor = PAGE_BG)\n\nax = Axis(\n    fig[1, 1];\n    title               = title_str,\n    titlesize           = title_size,\n    titlecolor          = INK,\n    subtitle            = \"EAN-13 · $(join(digits)) · Germany · retail product identification\",\n    subtitlesize        = 13,\n    subtitlecolor       = INK_SOFT,\n    backgroundcolor     = PAGE_BG,\n    xticksvisible       = false,\n    yticksvisible       = false,\n    xticklabelsvisible  = false,\n    yticklabelsvisible  = false,\n    topspinevisible     = false,\n    rightspinevisible   = false,\n    leftspinevisible    = false,\n    bottomspinevisible  = false,\n    xgridvisible        = false,\n    ygridvisible        = false,\n    xminorgridvisible   = false,\n    yminorgridvisible   = false,\n)\n\n# Vertical layout, top to bottom: bars -> human-readable digits ->\n# colored structure underlines -> segment names -> paper backdrop.\ntext_top      = -(guard_h + 8.0)\nunderline_y   = text_top - 34.0\nunderline_h   = 6.0\nseg_label_top = underline_y - underline_h - 10.0\npaper_top     = 18.0\npaper_bottom  = seg_label_top - 26.0\n\npoly!(ax, Rect2f(0.0, paper_bottom, total_width, paper_top - paper_bottom);\n      color = LABEL_BG, strokewidth = 0)\n\n# Bars: guard patterns (start, center, end) run deeper than data bars\nfor (bx, bh) in bars\n    poly!(ax, Rect2f(bx, -bh, 1.0, bh); color = BAR_INK, strokewidth = 0)\nend\n\n# Human-readable digits — the leading digit sits left of the start guard\ntext!(ax, quiet - 1.0, text_top; text = string(first_digit),\n      color = BAR_INK, fontsize = 24, align = (:right, :top))\nfor (i, d) in enumerate(left_digits)\n    text!(ax, left_start + (i - 1) * 7 + 3.5, text_top; text = string(d),\n          color = BAR_INK, fontsize = 24, align = (:center, :top))\nend\nfor (i, d) in enumerate(right_digits)\n    text!(ax, right_start + (i - 1) * 7 + 3.5, text_top; text = string(d),\n          color = BAR_INK, fontsize = 24, align = (:center, :top))\nend\n\n# Structure legend: country / manufacturer / product / check digit.\n# Imprint positions 1-3 in canonical order for the abstract groups; the\n# check digit gets the semantic-red anchor (position 5) instead of the\n# next ordinal slot, since its role is error detection, not an abstract\n# category (see \"Semantic exception\" in the default style guide).\nsegments = [\n    (2.0,                left_start + 7.0,   IMPRINT_PALETTE[1], \"Country\"),\n    (left_start + 7.0,   left_start + 42.0,  IMPRINT_PALETTE[2], \"Manufacturer\"),\n    (right_start,        right_start + 35.0, IMPRINT_PALETTE[3], \"Product\"),\n    (right_start + 35.0, right_start + 42.0, IMPRINT_PALETTE[5], \"Check\"),\n]\n\nfor (x1, x2, color, label) in segments\n    poly!(ax, Rect2f(x1, underline_y, x2 - x1, underline_h); color = color, strokewidth = 0)\n    text!(ax, (x1 + x2) / 2, seg_label_top; text = label,\n          color = PAPER_TEXT, fontsize = 15, align = (:center, :top))\nend\n\nxlims!(ax, -4.0, total_width + 4.0)\nylims!(ax, paper_bottom - 8.0, paper_top + 8.0)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}