Basic Data Matrix 2D Barcode — Pygal

A Data Matrix 2D barcode visualization that encodes data into a compact square or rectangular matrix of black and white cells. Data Matrix codes follow the ISO/IEC 16022 standard, featuring an L-shaped finder pattern (solid borders on two adjacent sides) and alternating timing patterns on the opposite sides. This barcode format is ideal for marking small items and supports high data density with built-in error correction (ECC 200).

Basic Data Matrix 2D Barcode rendered with Pygal

Python source (Pygal)

""" anyplot.ai
datamatrix-basic: Basic Data Matrix 2D Barcode
Library: pygal 3.1.0 | Python 3.13.13
Quality: 82/100 | Updated: 2026-05-20
"""

import os
import sys

import cairosvg
import numpy as np


# This file is named pygal.py, which shadows the installed package.
# Temporarily remove the script directory from sys.path so the real package loads.
_script_dir = os.path.dirname(os.path.abspath(__file__))
_removed = [p for p in list(sys.path) if p in ("", ".") or os.path.abspath(p) == _script_dir]
for _p in _removed:
    sys.path.remove(_p)

try:
    from pygal.graph.graph import Graph
    from pygal.style import Style
finally:
    for _p in _removed:
        sys.path.insert(0, _p)


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
IMPRINT = ("#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477")

# Data Matrix ECC 200 — symbol sizes: (rows, cols, data_codewords, ec_codewords)
SYMBOL_SIZES = [
    (10, 10, 3, 5),
    (12, 12, 5, 7),
    (14, 14, 8, 10),
    (16, 16, 12, 12),
    (18, 18, 18, 14),
    (20, 20, 22, 18),
    (22, 22, 30, 20),
    (24, 24, 36, 24),
    (26, 26, 44, 28),
]

# Galois field GF(256) tables — polynomial 0x12D (Data Matrix standard)
GF_EXP = [0] * 512
GF_LOG = [0] * 256
_x = 1
for _i in range(255):
    GF_EXP[_i] = _x
    GF_LOG[_x] = _i
    _x <<= 1
    if _x & 0x100:
        _x ^= 0x12D
for _i in range(255, 512):
    GF_EXP[_i] = GF_EXP[_i - 255]


def gf_mul(a, b):
    if a == 0 or b == 0:
        return 0
    return GF_EXP[(GF_LOG[a % 256] + GF_LOG[b % 256]) % 255]


def rs_encode(data, num_ec):
    data = [d % 256 for d in data]
    g = [1]
    for i in range(num_ec):
        new_g = [0] * (len(g) + 1)
        for j in range(len(g)):
            new_g[j] ^= gf_mul(g[j], GF_EXP[i])
            new_g[j + 1] ^= g[j]
        g = new_g
    encoded = list(data) + [0] * num_ec
    for i in range(len(data)):
        coef = encoded[i]
        if coef != 0:
            for j in range(len(g)):
                encoded[i + j] ^= gf_mul(g[j], coef)
    return encoded[len(data) :]


def generate_datamatrix(content):
    # ASCII encoding: each char → ordinal + 1
    codewords = [ord(c) + 1 for c in content if 0 <= ord(c) <= 127]

    # Select smallest fitting symbol size
    rows, cols, data_cap, ec_count = next((s for s in SYMBOL_SIZES if len(codewords) <= s[2]), SYMBOL_SIZES[-1])

    # Pad codewords to fill data capacity
    if len(codewords) < data_cap:
        codewords.append(129)
    while len(codewords) < data_cap:
        pad = 130 + (((149 * (len(codewords) + 1)) % 253) + 1) % 254
        codewords.append(pad)
    codewords = codewords[:data_cap]

    # Compute error correction and build full codeword stream
    all_codewords = codewords + rs_encode(codewords, ec_count)

    # Initialise matrix
    matrix = np.zeros((rows, cols), dtype=int)

    # L-shaped finder pattern: solid left column + solid bottom row
    matrix[:, 0] = 1
    matrix[rows - 1, :] = 1

    # Alternating timing patterns: top row and right column
    matrix[0, :] = np.arange(cols) % 2 == 0
    matrix[:, cols - 1] = np.arange(rows) % 2 == 0

    # Place data bits in the interior (column-major diagonal)
    data_rows, data_cols = rows - 2, cols - 2
    placed = np.zeros((data_rows, data_cols), dtype=bool)
    bit_idx = 0
    total_bits = len(all_codewords) * 8
    for module_num in range(data_rows * data_cols):
        if bit_idx >= total_bits:
            break
        r, c = module_num // data_cols, module_num % data_cols
        if not placed[r, c]:
            cw_idx, bit_pos = bit_idx // 8, 7 - (bit_idx % 8)
            if cw_idx < len(all_codewords):
                bit_value = (all_codewords[cw_idx] >> bit_pos) & 1
                ar, ac = r + 1, c + 1
                if 0 < ar < rows - 1 and 0 < ac < cols - 1:
                    matrix[ar, ac] = bit_value
            placed[r, c] = True
            bit_idx += 1

    return matrix


class DataMatrixChart(Graph):
    def __init__(self, *args, **kwargs):
        self.dm_data = kwargs.pop("dm_data", "ANYPLOT")
        self.module_color = kwargs.pop("module_color", "#1A1A17")
        self.cell_bg = kwargs.pop("cell_bg", "#FFFDF6")
        self.ink_color = kwargs.pop("ink_color", "#1A1A17")
        self.ink_soft_color = kwargs.pop("ink_soft_color", "#4A4A44")
        self.quiet_zone = kwargs.pop("quiet_zone", 2)
        super().__init__(*args, **kwargs)
        self._dm_matrix = None

    def _plot(self):
        self._dm_matrix = generate_datamatrix(self.dm_data)
        matrix_rows, matrix_cols = self._dm_matrix.shape
        total_rows = matrix_rows + 2 * self.quiet_zone
        total_cols = matrix_cols + 2 * self.quiet_zone

        plot_width = self.view.width
        plot_height = self.view.height
        margin = 160
        available_size = min(plot_width, plot_height) - 2 * margin
        cell_size = available_size / max(total_rows, total_cols)

        dm_width = total_cols * cell_size
        dm_height = total_rows * cell_size
        x_offset = self.view.x(0) + (plot_width - dm_width) / 2
        # Center vertically within the plot area
        y_offset = self.view.y(total_rows) + (plot_height - dm_height) / 2

        plot_node = self.nodes["plot"]
        dm_group = self.svg.node(plot_node, class_="datamatrix")

        # Barcode area background (always light for reliable scan contrast)
        bg_rect = self.svg.node(dm_group, "rect", x=x_offset, y=y_offset, width=dm_width, height=dm_height)
        bg_rect.set("fill", self.cell_bg)
        bg_rect.set("stroke", self.ink_soft_color)
        bg_rect.set("stroke-width", "2")

        # Draw filled modules
        for row in range(matrix_rows):
            for col in range(matrix_cols):
                if self._dm_matrix[row, col]:
                    x = x_offset + (col + self.quiet_zone) * cell_size
                    y = y_offset + (row + self.quiet_zone) * cell_size
                    rect = self.svg.node(dm_group, "rect", x=x, y=y, width=cell_size, height=cell_size)
                    rect.set("fill", self.module_color)

        # Labels below the barcode: encoded content, matrix spec, and context
        label_y = y_offset + dm_height + 70
        label_x = x_offset + dm_width / 2

        content_node = self.svg.node(dm_group, "text", x=label_x, y=label_y)
        content_node.set("text-anchor", "middle")
        content_node.set("fill", self.ink_color)
        content_node.set("style", "font-size:44px;font-weight:bold;font-family:sans-serif")
        content_node.text = f"Encoded: {self.dm_data}"

        info_node = self.svg.node(dm_group, "text", x=label_x, y=label_y + 58)
        info_node.set("text-anchor", "middle")
        info_node.set("fill", self.ink_soft_color)
        info_node.set("style", "font-size:36px;font-family:sans-serif")
        info_node.text = f"Matrix: {matrix_rows} × {matrix_cols} | ECC 200 | 30% error recovery"

        # Storytelling annotation: explains real-world significance
        story_node = self.svg.node(dm_group, "text", x=label_x, y=label_y + 110)
        story_node.set("text-anchor", "middle")
        story_node.set("fill", self.ink_soft_color)
        story_node.set("style", "font-size:30px;font-style:italic;font-family:sans-serif")
        story_node.text = "NDC codes identify drugs in the US pharmaceutical supply chain (FDA 21 CFR § 207)"

    def _compute(self):
        if self._dm_matrix is None:
            self._dm_matrix = generate_datamatrix(self.dm_data)
        matrix_rows, matrix_cols = self._dm_matrix.shape
        total_size = max(matrix_rows, matrix_cols) + 2 * self.quiet_zone
        self._box.xmin = 0
        self._box.xmax = total_size
        self._box.ymin = 0
        self._box.ymax = total_size


# Style
custom_style = Style(
    background=PAGE_BG,
    plot_background=PAGE_BG,
    foreground=INK,
    foreground_strong=INK,
    foreground_subtle=INK_SOFT,
    colors=IMPRINT,
    title_font_size=66,
    label_font_size=56,
    major_label_font_size=44,
    legend_font_size=44,
    value_font_size=36,
)

# Data — pharmaceutical NDC (National Drug Code) for drug authentication
dm_content = "NDC:0069-0069-20"

# Plot
chart = DataMatrixChart(
    width=2400,
    height=2400,
    style=custom_style,
    title="datamatrix-basic · python · pygal · anyplot.ai",
    dm_data=dm_content,
    module_color="#1A1A17",
    cell_bg="#FFFDF6",
    ink_color=INK,
    ink_soft_color=INK_SOFT,
    quiet_zone=2,
    show_legend=False,
    margin=100,
    margin_top=180,
    margin_bottom=180,
    show_x_labels=False,
    show_y_labels=False,
)

# Required: pygal's Graph._draw() only calls _plot() when series data is present
chart.add("", [0])

# Save
chart.render_to_file(f"plot-{THEME}.svg")
cairosvg.svg2png(url=f"plot-{THEME}.svg", write_to=f"plot-{THEME}.png", output_width=2400, output_height=2400)

svg_content = chart.render(is_unicode=True)
html_content = f"""<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>datamatrix-basic - python - pygal - anyplot.ai</title>
    <style>
        body {{
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: {PAGE_BG};
        }}
        .chart {{ max-width: 100%; height: auto; }}
    </style>
</head>
<body>
    <figure class="chart">
        {svg_content}
    </figure>
</body>
</html>
"""

with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
    f.write(html_content)

Part of Basic Data Matrix 2D Barcode on anyplot.ai.

Other implementations