{"spec_id":"barcode-ean13","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nbarcode-ean13: EAN-13 Barcode\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-21\n\"\"\"\n\nimport base64\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n\n# Remove this script's own directory from sys.path so the installed\n# bokeh package is found instead of this file (which is also named bokeh.py).\n_own_dir = os.path.dirname(os.path.realpath(__file__))\nsys.path = [p for p in sys.path if os.path.realpath(p or \".\") != _own_dir]\n\nfrom bokeh.embed import file_html\nfrom bokeh.models import ColumnDataSource, HoverTool, Label\nfrom bokeh.plotting import figure\nfrom bokeh.resources import INLINE\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# EAN-13 encoding patterns\nL_CODES = {\n    \"0\": [0, 0, 0, 1, 1, 0, 1],\n    \"1\": [0, 0, 1, 1, 0, 0, 1],\n    \"2\": [0, 0, 1, 0, 0, 1, 1],\n    \"3\": [0, 1, 1, 1, 1, 0, 1],\n    \"4\": [0, 1, 0, 0, 0, 1, 1],\n    \"5\": [0, 1, 1, 0, 0, 0, 1],\n    \"6\": [0, 1, 0, 1, 1, 1, 1],\n    \"7\": [0, 1, 1, 1, 0, 1, 1],\n    \"8\": [0, 1, 1, 0, 1, 1, 1],\n    \"9\": [0, 0, 0, 1, 0, 1, 1],\n}\n\nG_CODES = {\n    \"0\": [0, 1, 0, 0, 1, 1, 1],\n    \"1\": [0, 1, 1, 0, 0, 1, 1],\n    \"2\": [0, 0, 1, 1, 0, 1, 1],\n    \"3\": [0, 1, 0, 0, 0, 0, 1],\n    \"4\": [0, 0, 1, 1, 1, 0, 1],\n    \"5\": [0, 1, 1, 1, 0, 0, 1],\n    \"6\": [0, 0, 0, 0, 1, 0, 1],\n    \"7\": [0, 0, 1, 0, 0, 0, 1],\n    \"8\": [0, 0, 0, 1, 0, 0, 1],\n    \"9\": [0, 0, 1, 0, 1, 1, 1],\n}\n\nR_CODES = {\n    \"0\": [1, 1, 1, 0, 0, 1, 0],\n    \"1\": [1, 1, 0, 0, 1, 1, 0],\n    \"2\": [1, 1, 0, 1, 1, 0, 0],\n    \"3\": [1, 0, 0, 0, 0, 1, 0],\n    \"4\": [1, 0, 1, 1, 1, 0, 0],\n    \"5\": [1, 0, 0, 1, 1, 1, 0],\n    \"6\": [1, 0, 1, 0, 0, 0, 0],\n    \"7\": [1, 0, 0, 0, 1, 0, 0],\n    \"8\": [1, 0, 0, 1, 0, 0, 0],\n    \"9\": [1, 1, 1, 0, 1, 0, 0],\n}\n\nFIRST_DIGIT_PATTERNS = {\n    \"0\": \"LLLLLL\",\n    \"1\": \"LLGLGG\",\n    \"2\": \"LLGGLG\",\n    \"3\": \"LLGGGL\",\n    \"4\": \"LGLLGG\",\n    \"5\": \"LGGLLG\",\n    \"6\": \"LGGGLL\",\n    \"7\": \"LGLGLG\",\n    \"8\": \"LGLGGL\",\n    \"9\": \"LGGLGL\",\n}\n\nSTART_GUARD = [1, 0, 1]\nCENTER_GUARD = [0, 1, 0, 1, 0]\nEND_GUARD = [1, 0, 1]\n\n# 12-digit German product code — check digit auto-calculated\ncode = \"400638133393\"\n\ntotal = 0\nfor pos, digit in enumerate(code):\n    if pos % 2 == 0:\n        total += int(digit)\n    else:\n        total += int(digit) * 3\ncheck_digit = (10 - (total % 10)) % 10\ncode = code + str(check_digit)\n\n# Build barcode bit pattern with section labels for HoverTool\nbarcode_pattern = []\nsection_labels = []\n\nfor bit in START_GUARD:\n    barcode_pattern.append(bit)\n    section_labels.append(\"Start Guard\")\n\nlg_pattern = FIRST_DIGIT_PATTERNS[code[0]]\nfor i, digit in enumerate(code[1:7]):\n    label = f\"Left digit {i + 1}: '{digit}'\"\n    if lg_pattern[i] == \"L\":\n        bits = L_CODES[digit]\n    else:\n        bits = G_CODES[digit]\n    for bit in bits:\n        barcode_pattern.append(bit)\n        section_labels.append(label)\n\nfor bit in CENTER_GUARD:\n    barcode_pattern.append(bit)\n    section_labels.append(\"Center Guard\")\n\nfor i, digit in enumerate(code[7:13]):\n    label = f\"Right digit {i + 1}: '{digit}'\"\n    for bit in R_CODES[digit]:\n        barcode_pattern.append(bit)\n        section_labels.append(label)\n\nfor bit in END_GUARD:\n    barcode_pattern.append(bit)\n    section_labels.append(\"End Guard\")\n\n# Canvas dimensions (canonical Bokeh landscape)\nW, H = 3200, 1800\n\n# Scale barcode to ~70% of canvas width\nquiet_zone_modules = 11\ntotal_modules = quiet_zone_modules + len(barcode_pattern) + quiet_zone_modules\nmodule_width = int(W * 0.70 / total_modules)\nquiet_zone = quiet_zone_modules * module_width\n\n# Bar heights in data coordinates\nbar_height = 1100\nguard_height = 1300\ntext_y_pos = 80\n\n# Guard bar module index ranges\nstart_guard_end = 2\ncenter_guard_start = 3 + 6 * 7\ncenter_guard_end = center_guard_start + 4\nend_guard_start = center_guard_end + 1 + 6 * 7\n\nbar_lefts = []\nbar_widths_px = []\nbar_tops = []\nbar_bottoms = []\nbar_sections = []\n\nx_pos = quiet_zone\nmodule_idx = 0\ni = 0\n\nwhile i < len(barcode_pattern):\n    if barcode_pattern[i] == 1:\n        bar_start = x_pos\n        bar_w = 0\n        start_module = module_idx\n        sec = section_labels[i]\n\n        while i < len(barcode_pattern) and barcode_pattern[i] == 1:\n            bar_w += module_width\n            x_pos += module_width\n            module_idx += 1\n            i += 1\n\n        is_guard = (\n            start_module <= start_guard_end\n            or center_guard_start <= start_module <= center_guard_end\n            or start_module >= end_guard_start\n        )\n\n        bar_lefts.append(bar_start)\n        bar_widths_px.append(bar_w)\n        bar_sections.append(sec)\n        if is_guard:\n            bar_tops.append(guard_height + 150)\n            bar_bottoms.append(150)\n        else:\n            bar_tops.append(bar_height + 150)\n            bar_bottoms.append(280)\n    else:\n        x_pos += module_width\n        module_idx += 1\n        i += 1\n\ntotal_barcode_width = x_pos + quiet_zone\nx_offset = (W - total_barcode_width) / 2\nbar_lefts = [left + x_offset for left in bar_lefts]\nbar_rights = [left + w for left, w in zip(bar_lefts, bar_widths_px, strict=True)]\n\n# Figure — y_range extended to accommodate zone annotations above the barcode\np = figure(\n    width=W,\n    height=H,\n    title=\"barcode-ean13 · python · bokeh · anyplot.ai\",\n    x_range=(0, W),\n    y_range=(0, guard_height + 580),\n    toolbar_location=None,\n    min_border_top=110,\n    min_border_bottom=80,\n    min_border_left=80,\n    min_border_right=50,\n)\n\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = None\n\np.title.text_font_size = \"50pt\"\np.title.align = \"center\"\np.title.text_color = INK\np.title.text_font_style = \"normal\"\n\np.xaxis.visible = False\np.yaxis.visible = False\np.xgrid.visible = False\np.ygrid.visible = False\n\nsource = ColumnDataSource(\n    data={\"left\": bar_lefts, \"right\": bar_rights, \"top\": bar_tops, \"bottom\": bar_bottoms, \"section\": bar_sections}\n)\n\nbars = p.quad(left=\"left\", right=\"right\", top=\"top\", bottom=\"bottom\", color=INK, source=source)\n\n# HoverTool reveals which digit or guard section each bar encodes\nhover = HoverTool(renderers=[bars], tooltips=[(\"Encodes\", \"@section\")])\np.add_tools(hover)\n\n# Human-readable digits: first digit outside left guard\nfirst_digit_x = x_offset + quiet_zone - module_width * 5\nleft_start = x_offset + quiet_zone + 3 * module_width\nleft_span = 6 * 7 * module_width\nright_start = left_start + left_span + 5 * module_width\nright_span = 6 * 7 * module_width\n\np.add_layout(\n    Label(\n        x=first_digit_x,\n        y=text_y_pos,\n        text=code[0],\n        text_font_size=\"48pt\",\n        text_align=\"center\",\n        text_baseline=\"bottom\",\n        text_color=INK,\n        text_font=\"monospace\",\n    )\n)\n\np.add_layout(\n    Label(\n        x=left_start + left_span / 2,\n        y=text_y_pos,\n        text=code[1:7],\n        text_font_size=\"48pt\",\n        text_align=\"center\",\n        text_baseline=\"bottom\",\n        text_color=INK,\n        text_font=\"monospace\",\n    )\n)\n\np.add_layout(\n    Label(\n        x=right_start + right_span / 2,\n        y=text_y_pos,\n        text=code[7:13],\n        text_font_size=\"48pt\",\n        text_align=\"center\",\n        text_baseline=\"bottom\",\n        text_color=INK,\n        text_font=\"monospace\",\n    )\n)\n\n# Zone annotations: bracket lines + labels above the barcode showing structure\n# EAN-13 structure: Country (400) | Manufacturer (6381) | Product (33393) | Check (1)\nzone_line_y = guard_height + 200  # just above guard bar tops\nzone_name_y = zone_line_y + 60  # zone name text y\nzone_val_y = zone_line_y + 160  # zone digit value y\n\ncountry_x1 = first_digit_x - module_width * 3\ncountry_x2 = left_start + 2 * 7 * module_width\nmfr_x1 = country_x2\nmfr_x2 = left_start + left_span\nprod_x1 = right_start\nprod_x2 = right_start + 5 * 7 * module_width\ncheck_x1 = prod_x2\ncheck_x2 = right_start + right_span\n\nzones = [\n    (country_x1, country_x2, \"Country\", code[:3]),\n    (mfr_x1, mfr_x2, \"Manufacturer\", code[3:7]),\n    (prod_x1, prod_x2, \"Product\", code[7:12]),\n    (check_x1, check_x2, \"Check\", code[12]),\n]\n\nfor x1, x2, zone_name, zone_val in zones:\n    cx = (x1 + x2) / 2\n    pad = module_width\n    # Horizontal bracket line\n    p.segment(x0=[x1 + pad], y0=[zone_line_y], x1=[x2 - pad], y1=[zone_line_y], line_color=INK_SOFT, line_width=2)\n    # Vertical end ticks\n    p.segment(\n        x0=[x1 + pad], y0=[zone_line_y - 22], x1=[x1 + pad], y1=[zone_line_y + 22], line_color=INK_SOFT, line_width=2\n    )\n    p.segment(\n        x0=[x2 - pad], y0=[zone_line_y - 22], x1=[x2 - pad], y1=[zone_line_y + 22], line_color=INK_SOFT, line_width=2\n    )\n    # Zone name\n    p.add_layout(\n        Label(\n            x=cx,\n            y=zone_name_y,\n            text=zone_name,\n            text_align=\"center\",\n            text_baseline=\"bottom\",\n            text_color=INK_SOFT,\n            text_font_size=\"26pt\",\n            text_font=\"monospace\",\n        )\n    )\n    # Zone digit value\n    p.add_layout(\n        Label(\n            x=cx,\n            y=zone_val_y,\n            text=zone_val,\n            text_align=\"center\",\n            text_baseline=\"bottom\",\n            text_color=INK,\n            text_font_size=\"36pt\",\n            text_font=\"monospace\",\n        )\n    )\n\n# Save interactive HTML with inline resources (no CDN dependency)\nhtml_path = Path(f\"plot-{THEME}.html\").resolve()\nhtml_content = file_html(p, INLINE)\n# Inject body background + CSS to eliminate any canvas/border artifacts.\n# The body background fix prevents browser-default white from bleeding through\n# as a thin border in the dark theme; the CSS removes Bokeh canvas box-shadow.\nhtml_content = html_content.replace(\"<body>\", f'<body style=\"background-color:{PAGE_BG};margin:0;padding:0;\">', 1)\n# Set html element background + remove any canvas/div borders/shadows.\n# html{} is needed because Chrome uses the html element's background to fill\n# the viewport even outside the body, causing 1-px edge artifacts in headless.\nhtml_content = html_content.replace(\n    \"</head>\",\n    (\n        f\"<style>\"\n        f\"html{{background-color:{PAGE_BG}!important;margin:0!important;padding:0!important;}}\"\n        f\"canvas,div.bk-root,div.bk{{border:none!important;box-shadow:none!important;outline:none!important;}}\"\n        f\"</style></head>\"\n    ),\n    1,\n)\nhtml_path.write_text(html_content)\n\n# Screenshot with headless Chrome via Selenium + CDP for exact W×H dimensions\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{html_path}\")\ntime.sleep(3)\nresult = driver.execute_cdp_cmd(\n    \"Page.captureScreenshot\",\n    {\"format\": \"png\", \"captureBeyondViewport\": True, \"clip\": {\"x\": 0, \"y\": 0, \"width\": W, \"height\": H, \"scale\": 1.0}},\n)\nPath(f\"plot-{THEME}.png\").write_bytes(base64.b64decode(result[\"data\"]))\ndriver.quit()\n"}