EAN-13 Barcode — plotnine

A standard EAN-13 barcode visualization commonly used for retail products worldwide. The plot renders a scannable linear barcode encoding a 13-digit number with proper bar widths, guard patterns, and human-readable digits. This visualization is essential for product identification, inventory management, and point-of-sale systems.

EAN-13 Barcode rendered with plotnine

Python source (plotnine)

""" anyplot.ai
barcode-ean13: EAN-13 Barcode
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 86/100 | Updated: 2026-05-21
"""

import os

import pandas as pd
from plotnine import (
    aes,
    coord_cartesian,
    element_rect,
    element_text,
    geom_rect,
    geom_text,
    ggplot,
    labs,
    theme,
    theme_void,
)


THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"

# EAN-13 encoding patterns
L_CODES = {
    "0": "0001101",
    "1": "0011001",
    "2": "0010011",
    "3": "0111101",
    "4": "0100011",
    "5": "0110001",
    "6": "0101111",
    "7": "0111011",
    "8": "0110111",
    "9": "0001011",
}
G_CODES = {
    "0": "0100111",
    "1": "0110011",
    "2": "0011011",
    "3": "0100001",
    "4": "0011101",
    "5": "0111001",
    "6": "0000101",
    "7": "0010001",
    "8": "0001001",
    "9": "0010111",
}
R_CODES = {
    "0": "1110010",
    "1": "1100110",
    "2": "1101100",
    "3": "1000010",
    "4": "1011100",
    "5": "1001110",
    "6": "1010000",
    "7": "1000100",
    "8": "1001000",
    "9": "1110100",
}
FIRST_DIGIT_PATTERN = {
    "0": "LLLLLL",
    "1": "LLGLGG",
    "2": "LLGGLG",
    "3": "LLGGGL",
    "4": "LGLLGG",
    "5": "LGGLLG",
    "6": "LGGGLL",
    "7": "LGLGLG",
    "8": "LGLGGL",
    "9": "LGGLGL",
}

# Data: German product EAN-13 code
code = "4006381333931"

if len(code) == 12:
    odd_sum = sum(int(code[i]) for i in range(0, 12, 2))
    even_sum = sum(int(code[i]) for i in range(1, 12, 2))
    check = (10 - (odd_sum + even_sum * 3) % 10) % 10
    code = code + str(check)

first_digit = code[0]
left_digits = code[1:7]
right_digits = code[7:13]
parity_pattern = FIRST_DIGIT_PATTERN[first_digit]

binary = "101"
for i, digit in enumerate(left_digits):
    binary += L_CODES[digit] if parity_pattern[i] == "L" else G_CODES[digit]
binary += "01010"
for digit in right_digits:
    binary += R_CODES[digit]
binary += "101"

# Build bar rectangles (guards extend to ymin=0, data bars start at ymin=5)
quiet_zone = 9
bars = []
x_pos = quiet_zone
for i, bit in enumerate(binary):
    if bit == "1":
        is_guard = i < 3 or i >= len(binary) - 3 or (45 <= i < 50)
        bars.append({"xmin": x_pos, "xmax": x_pos + 1, "ymin": 0 if is_guard else 5, "ymax": 70})
    x_pos += 1

df_bars = pd.DataFrame(bars)
total_width = quiet_zone * 2 + len(binary)

# Digit label positions
digit_labels = [{"x": quiet_zone - 4, "y": -8, "label": first_digit}]
left_start = quiet_zone + 3
for i, digit in enumerate(left_digits):
    digit_labels.append({"x": left_start + i * 7 + 3.5, "y": -8, "label": digit})
right_start = quiet_zone + 3 + 42 + 5
for i, digit in enumerate(right_digits):
    digit_labels.append({"x": right_start + i * 7 + 3.5, "y": -8, "label": digit})

df_labels = pd.DataFrame(digit_labels)

# Plot
plot = (
    ggplot()
    + geom_rect(df_bars, aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax"), fill=INK, color=INK)
    + geom_text(df_labels, aes(x="x", y="y", label="label"), size=14, color=INK, family="monospace", fontweight="bold")
    + labs(title="barcode-ean13 · python · plotnine · anyplot.ai")
    + coord_cartesian(xlim=(-2, total_width + 2), ylim=(-14, 74))
    + theme_void()
    + theme(
        figure_size=(8, 4.5),
        plot_title=element_text(size=12, color=INK, ha="center", margin={"t": 6, "b": 2}),
        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        panel_background=element_rect(fill=PAGE_BG),
    )
)

plot.save(f"plot-{THEME}.png", dpi=400, width=8, height=4.5, units="in")

Part of EAN-13 Barcode on anyplot.ai.

Other implementations