{"spec_id":"barcode-ean13","library":"d3","language":"javascript","code":"// anyplot.ai\n// barcode-ean13: EAN-13 Barcode\n// Library: d3 7.9.0 | JavaScript 22.23.2\n// Quality: 84/100 | Created: 2026-09-01\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\n\n// --- EAN-13 encoding tables --------------------------------------------------\n// Left-hand digit patterns: \"L\" (odd parity) and \"G\" (even parity), 7 modules each.\nconst L_CODE = [\"0001101\", \"0011001\", \"0010011\", \"0111101\", \"0100011\", \"0110001\", \"0101111\", \"0111011\", \"0110111\", \"0001011\"];\nconst G_CODE = [\"0100111\", \"0110011\", \"0011011\", \"0100001\", \"0011101\", \"0111001\", \"0000101\", \"0010001\", \"0001001\", \"0010111\"];\nconst R_CODE = [\"1110010\", \"1100110\", \"1101100\", \"1000010\", \"1011100\", \"1001110\", \"1010000\", \"1000100\", \"1001000\", \"1110100\"];\n// Parity pattern (L/G) for the 6 left digits, selected by the leading digit.\nconst PARITY = [\"LLLLLL\", \"LLGLGG\", \"LLGGLG\", \"LLGGGL\", \"LGLLGG\", \"LGGLLG\", \"LGGGLL\", \"LGLGLG\", \"LGLGGL\", \"LGGLGL\"];\n\n// --- Data: a 12-digit product code, check digit auto-calculated -------------\nconst productCode = \"590123412345\";\nconst digits = productCode.split(\"\").map(Number);\nconst checksum = (10 - digits.reduce((sum, d, i) => sum + d * (i % 2 === 0 ? 1 : 3), 0) % 10) % 10;\nconst code = digits.concat(checksum);\n\nconst leftDigits = code.slice(1, 7);\nconst rightDigits = code.slice(7, 13);\nconst parity = PARITY[code[0]];\nconst leftBits = leftDigits.map((d, i) => (parity[i] === \"L\" ? L_CODE[d] : G_CODE[d])).join(\"\");\nconst rightBits = rightDigits.map((d) => R_CODE[d]).join(\"\");\n\nconst QUIET = 9;\nconst bits = \"0\".repeat(QUIET) + \"101\" + leftBits + \"01010\" + rightBits + \"101\" + \"0\".repeat(QUIET);\nconst START_GUARD = [QUIET, QUIET + 2];\nconst CENTER_GUARD = [QUIET + 45, QUIET + 49];\nconst END_GUARD = [QUIET + 92, QUIET + 94];\nconst inGuard = (i) => (i >= START_GUARD[0] && i <= START_GUARD[1]) || (i >= CENTER_GUARD[0] && i <= CENTER_GUARD[1]) || (i >= END_GUARD[0] && i <= END_GUARD[1]);\n\n// A physical barcode must stay dark-ink-on-light-background to remain\n// scannable regardless of the page's light/dark theme — only the\n// surrounding chrome (title) is allowed to flip with ANYPLOT_THEME.\nconst BARCODE_BG = \"#FAF8F1\";\nconst BARCODE_BORDER = \"#D9D4C3\";\nconst BARCODE_INK = \"#1A1A17\";\n\n// --- Layout -------------------------------------------------------------------\nconst moduleWidth = 11;\nconst barcodeWidth = bits.length * moduleWidth;\nconst startX = (width - barcodeWidth) / 2;\nconst digitBarHeight = 420;\nconst guardBarHeight = digitBarHeight + 40;\nconst barsTopY = 260;\n\nconst cardPad = 60;\nconst cardTop = barsTopY - 50;\nconst textY = barsTopY + guardBarHeight + 50;\nconst cardBottom = textY + 40;\n\n// --- SVG mount ----------------------------------------------------------------\nconst svg = d3.select(\"#container\").append(\"svg\").attr(\"width\", width).attr(\"height\", height);\n\n// Label card — barcodes read as printed labels, so an elevated card grounds\n// it; the card stays fixed light regardless of theme (see BARCODE_* above).\nsvg.append(\"rect\")\n  .attr(\"x\", startX - cardPad).attr(\"y\", cardTop)\n  .attr(\"width\", barcodeWidth + cardPad * 2).attr(\"height\", cardBottom - cardTop)\n  .attr(\"rx\", 24)\n  .attr(\"fill\", BARCODE_BG)\n  .attr(\"stroke\", BARCODE_BORDER)\n  .attr(\"stroke-width\", 1.5);\n\n// Bars — one rect per \"1\" module, bound via a data-join with a linear scale\n// mapping module index to pixel offset; guard bars extend further down as\n// sync marks.\nconst moduleX = d3.scaleLinear().domain([0, bits.length]).range([0, barcodeWidth]);\nconst moduleW = moduleX(1) - moduleX(0);\nconst barData = [...bits]\n  .map((bit, i) => ({ bit, i, guard: inGuard(i) }))\n  .filter((d) => d.bit === \"1\");\n\nsvg.append(\"g\")\n  .selectAll(\"rect\")\n  .data(barData)\n  .join(\"rect\")\n  .attr(\"x\", (d) => startX + moduleX(d.i))\n  .attr(\"y\", barsTopY)\n  .attr(\"width\", moduleW)\n  .attr(\"height\", (d) => (d.guard ? guardBarHeight : digitBarHeight))\n  .attr(\"fill\", BARCODE_INK);\n\n// Human-readable digits — leading digit sits left of the start guard; the two\n// six-digit groups center under their own encoded region.\nconst digitFont = { fontFamily: \"ui-monospace, 'SFMono-Regular', Menlo, monospace\", fontSize: \"30px\", fill: BARCODE_INK };\n\nsvg.append(\"text\")\n  .attr(\"x\", startX + (START_GUARD[0] - 1) * moduleWidth).attr(\"y\", textY)\n  .attr(\"text-anchor\", \"end\")\n  .style(\"font-family\", digitFont.fontFamily).style(\"font-size\", digitFont.fontSize).attr(\"fill\", digitFont.fill)\n  .text(code[0]);\n\nsvg.append(\"text\")\n  .attr(\"x\", startX + (START_GUARD[1] + 1 + CENTER_GUARD[0] - 1) / 2 * moduleWidth).attr(\"y\", textY)\n  .attr(\"text-anchor\", \"middle\")\n  .style(\"font-family\", digitFont.fontFamily).style(\"font-size\", digitFont.fontSize).attr(\"fill\", digitFont.fill)\n  .style(\"letter-spacing\", \"6px\")\n  .text(leftDigits.join(\"\"));\n\nsvg.append(\"text\")\n  .attr(\"x\", startX + (CENTER_GUARD[1] + 1 + END_GUARD[0] - 1) / 2 * moduleWidth).attr(\"y\", textY)\n  .attr(\"text-anchor\", \"middle\")\n  .style(\"font-family\", digitFont.fontFamily).style(\"font-size\", digitFont.fontSize).attr(\"fill\", digitFont.fill)\n  .style(\"letter-spacing\", \"6px\")\n  .text(rightDigits.join(\"\"));\n\n// Title\nsvg.append(\"text\").attr(\"x\", width / 2).attr(\"y\", 64).attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink).style(\"font-size\", \"26px\").style(\"font-weight\", \"600\")\n  .text(\"barcode-ean13 · javascript · d3 · anyplot.ai\");\n"}