{"spec_id":"barcode-ean13","library":"echarts","language":"javascript","code":"// anyplot.ai\n// barcode-ean13: EAN-13 Barcode\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-01\n\nconst t = window.ANYPLOT_TOKENS;\nconst W = window.ANYPLOT_SIZE.width;\nconst H = window.ANYPLOT_SIZE.height;\n\n// --- EAN-13 encoding tables --------------------------------------------------\nconst L_CODE = [\n  \"0001101\", \"0011001\", \"0010011\", \"0111101\", \"0100011\",\n  \"0110001\", \"0101111\", \"0111011\", \"0110111\", \"0001011\",\n];\nconst G_CODE = [\n  \"0100111\", \"0110011\", \"0011011\", \"0100001\", \"0011101\",\n  \"0111001\", \"0000101\", \"0010001\", \"0001001\", \"0010111\",\n];\nconst R_CODE = L_CODE.map((code) => code.replace(/./g, (b) => (b === \"0\" ? \"1\" : \"0\")));\nconst FIRST_DIGIT_PARITY = [\n  \"LLLLLL\", \"LLGLGG\", \"LLGGLG\", \"LLGGGL\", \"LGLLGG\",\n  \"LGGLLG\", \"LGGGLL\", \"LGLGLG\", \"LGLGGL\", \"LGGLGL\",\n];\n\n// digits[0..11] = manufacturer-side base digits; the 13th (check) digit is derived\nfunction checkDigit(digits) {\n  const sum = digits.reduce((acc, d, i) => acc + d * (i % 2 === 0 ? 1 : 3), 0);\n  return (10 - (sum % 10)) % 10;\n}\n\nfunction encodeEan13(baseDigits) {\n  const digits = baseDigits.concat(checkDigit(baseDigits));\n  const first = digits[0];\n  const left = digits.slice(1, 7);\n  const right = digits.slice(7, 13);\n  const parity = FIRST_DIGIT_PARITY[first];\n  const leftBits = left.map((d, i) => (parity[i] === \"L\" ? L_CODE[d] : G_CODE[d])).join(\"\");\n  const rightBits = right.map((d) => R_CODE[d]).join(\"\");\n  const bits = \"101\" + leftBits + \"01010\" + rightBits + \"101\";\n  return { digits, bits };\n}\n\n// --- Data (in-memory, deterministic) -----------------------------------------\n// 5901234123457 — Polish specialty-coffee product code (12 base digits + computed check digit)\nconst { digits, bits } = encodeEan13([5, 9, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5]);\nconst firstDigit = digits[0];\nconst leftDigits = digits.slice(1, 7);\nconst rightDigits = digits.slice(7, 13);\n\n// --- Layout -------------------------------------------------------------------\nconst QUIET = 9; // quiet-zone modules on each side (spec minimum)\nconst MODULE_W = 12;\nconst DIGIT_BAR_H = 380;\nconst GUARD_BAR_H = DIGIT_BAR_H + 50;\nconst PAD = 50;\nconst TEXT_GAP = 20;\nconst TEXT_H = 56;\n\nconst cardW = PAD * 2 + (QUIET * 2 + bits.length) * MODULE_W;\nconst cardH = PAD * 2 + GUARD_BAR_H + TEXT_GAP + TEXT_H;\nconst titleArea = 130;\nconst cardX = (W - cardW) / 2;\nconst cardY = titleArea + (H - titleArea - cardH) / 2;\nconst barsTop = cardY + PAD;\nconst barsLeft = cardX + PAD;\nconst textY = barsTop + GUARD_BAR_H + TEXT_GAP;\n\n// Real barcodes stay black-on-white regardless of site theme — a scanner reads\n// contrast, not brand chrome — so the label card and its bars use fixed paper\n// colors while the surrounding page and title stay theme-adaptive.\nconst LABEL_BG = \"#FDFCF5\";\nconst LABEL_BORDER = t.inkSoft;\nconst BAR_COLOR = \"#1A1A17\";\n\n// Guard-bar module ranges (start, center, end) render taller than digit bars\nfunction isGuardModule(idx) {\n  return idx < 3 || (idx >= 45 && idx < 50) || idx >= 92;\n}\n\n// Collapse the module bit-string into contiguous black bars (runs of \"1\")\nfunction buildBars(bitString) {\n  const bars = [];\n  let runStart = -1;\n  for (let idx = 0; idx <= bitString.length; idx += 1) {\n    const on = bitString[idx] === \"1\";\n    if (on && runStart === -1) runStart = idx;\n    if (!on && runStart !== -1) {\n      bars.push({ start: runStart, width: idx - runStart, tall: isGuardModule(runStart) });\n      runStart = -1;\n    }\n  }\n  return bars;\n}\n\nconst barRects = buildBars(bits).map((bar) => ({\n  type: \"rect\",\n  shape: {\n    x: barsLeft + (QUIET + bar.start) * MODULE_W,\n    y: barsTop,\n    width: bar.width * MODULE_W,\n    height: bar.tall ? GUARD_BAR_H : DIGIT_BAR_H,\n  },\n  style: { fill: BAR_COLOR },\n}));\n\nfunction digitText(x, text) {\n  return {\n    type: \"text\",\n    x,\n    y: textY,\n    style: {\n      text,\n      fill: BAR_COLOR,\n      fontSize: 32,\n      fontFamily: \"Menlo, Consolas, monospace\",\n      textAlign: \"center\",\n      textVerticalAlign: \"top\",\n    },\n  };\n}\n\nconst digitTexts = [\n  digitText(barsLeft + (QUIET / 2) * MODULE_W, String(firstDigit)),\n  ...leftDigits.map((d, i) =>\n    digitText(barsLeft + (QUIET + 3 + i * 7 + 3.5) * MODULE_W, String(d))\n  ),\n  ...rightDigits.map((d, i) =>\n    digitText(barsLeft + (QUIET + 50 + i * 7 + 3.5) * MODULE_W, String(d))\n  ),\n];\n\n// --- Init -----------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -----------------------------------------------------------------\nconst title = \"Specialty Coffee Beans 250g · barcode-ean13 · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.max(15, Math.round(22 * Math.min(1, 67 / title.length)));\n\nconst gtin = digits.join(\"\");\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  tooltip: {},\n  title: {\n    text: title,\n    left: \"center\",\n    top: 40,\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n  },\n  graphic: {\n    elements: [\n      {\n        type: \"rect\",\n        shape: { x: cardX, y: cardY, width: cardW, height: cardH, r: 6 },\n        style: { fill: LABEL_BG, stroke: LABEL_BORDER, lineWidth: 1.5, shadowBlur: 18, shadowColor: \"rgba(0,0,0,0.18)\", shadowOffsetY: 6 },\n        // Graphic elements can carry their own tooltip config (ECharts-specific,\n        // not a generic canvas capability); hover over the card to reveal the\n        // GTIN this barcode encodes — purely an interactive-HTML affordance,\n        // invisible to the static screenshot.\n        tooltip: { show: true, formatter: `GTIN: ${gtin}` },\n      },\n      ...barRects,\n      ...digitTexts,\n    ],\n  },\n});\n"}