Source code for pystrich.pdf417

"""PDF417 2D barcode encoder.

Typical use::

    encoder = PDF417Encoder("Hello, World!", ecl=2)
    encoder.save("hello.png")
    encoder.save_svg("hello.svg")

The :class:`PDF417Encoder` constructor picks sensible defaults: an
error correction level matching the spec's recommendation for the
data length when ``ecl`` is omitted, a near-square matrix shape when
``columns`` is omitted, and the narrowest fitting character encoding
when the input is a plain ``str``. Set ``row_height`` to change the
default Y/X = 3 row aspect; wrap the input in :class:`PDF417Data` to
pin the character encoding by hand.

Macro PDF417, Compact PDF417, and optimal mode-switching are not
implemented.
"""

from __future__ import annotations

from typing import Literal

from pystrich.matrix_encoder import Matrix2DEncoder

from .data import PDF417Data, PDF417Encoding
from .layout import DEFAULT_ROW_HEIGHT, build_module_matrix
from .renderer import PDF417_DEFAULT_QUIET_ZONE, PDF417Renderer
from .textencoder import _compact, assemble, pick_dimensions

PDF417ErrorCorrectionLevel = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8]
"""PDF417 error correction levels."""

__all__ = [
    "DEFAULT_ROW_HEIGHT",
    "PDF417_DEFAULT_QUIET_ZONE",
    "PDF417Data",
    "PDF417Encoder",
    "PDF417Encoding",
    "PDF417ErrorCorrectionLevel",
]


def _auto_ecl(source_count: int) -> int:
    """Recommend a minimum error correction level for ``source_count`` codewords."""
    if source_count <= 40:
        return 2
    if source_count <= 160:
        return 3
    if source_count <= 320:
        return 4
    return 5


[docs] class PDF417Encoder(Matrix2DEncoder[int]): """Encode text as a PDF417 2D barcode. The matrix shape is determined by the data length, the error correction level, and an optional explicit column count. When ``ecl`` is omitted, it is chosen to match the spec's minimum-level recommendation for the given data length. :ivar matrix: 0/1 module grid built from the codeword stream. :ivar rows: Number of codeword rows in the symbol. :ivar columns: Number of data columns (excluding row indicators). :ivar ecl: Error correction level in effect. :ivar row_height: Matrix rows per codeword row (default 3). :ivar quiet_zone: White border applied by the renderer, in modules. """ rows: int columns: int ecl: int row_height: int quiet_zone: int def __init__( self, text: PDF417Data | str, *, ecl: PDF417ErrorCorrectionLevel | None = None, columns: int | None = None, quiet_zone: int = PDF417_DEFAULT_QUIET_ZONE, row_height: int = DEFAULT_ROW_HEIGHT, ) -> None: source = _compact(text) ecl_value: int = _auto_ecl(len(source) + 1) if ecl is None else ecl rows, columns = pick_dimensions(len(source), ecl_value, columns) codewords = assemble(source, rows, columns, ecl_value) self.width = 0 self.height = 0 self.rows = rows self.columns = columns self.ecl = ecl_value self.row_height = row_height self.quiet_zone = quiet_zone self.matrix = build_module_matrix( codewords, rows, columns, ecl_value, row_height=row_height )
[docs] def init_renderer(self) -> PDF417Renderer: """Construct a :class:`PDF417Renderer` for the encoded matrix. Wraps the matrix in a quiet zone before handing it back and updates :attr:`width` and :attr:`height` to the renderer's module dimensions. """ renderer = PDF417Renderer( self.matrix, quiet_zone=self.quiet_zone, row_height=self.row_height ) self.width = renderer.width self.height = renderer.height return renderer