PDF417 ****** PDF417 is a stacked 2D symbology used on driver's licences, boarding passes, shipping labels and other documents that need a high-capacity barcode (up to 925 data codewords). See also: PDF417 on Wikipedia for background on the symbology itself. PDF417 is defined in ISO/IEC 15438. Example ======= from pystrich.pdf417 import PDF417Encoder encoder = PDF417Encoder("WDBCA45D2HA327260") encoder.save_svg("pdf417-example.svg") [image: PDF417 encoding "WDBCA45D2HA327260".][image] Sizing and quiet zone ===================== The "cellsize" argument to "save()" and "get_imagedata()" sets the pixel side length of one module (default "5"). The "quiet_zone" argument to "PDF417Encoder" sets the width (in modules) of the white border applied at render time. The PDF417 specification requires at least two modules on every side; pyStrich defaults to that minimum. PDF417 modules are not square -- the spec recommends a row height of at least three times the module width ("Y >= 3X"). The "row_height" argument expresses that ratio; the default "3" matches the spec recommendation. Decreasing it produces a denser symbol but reduces scanner robustness. See also: Printing barcodes for guidance on selecting "cellsize" for printed output. encoder = PDF417Encoder("WDBCA45D2HA327260") encoder.save("pdf417-large.png", cellsize=10) [image: PDF417 encoding "WDBCA45D2HA327260" rendered with cellsize=10.][image] Output formats ============== SVG output ---------- For embedding in web pages or any workflow that benefits from resolution-independent output, use "save_svg()" (or "get_svg()" to receive the SVG as a string). PDF417Encoder("WDBCA45D2HA327260").save_svg("pdf417.svg") The SVG's "viewBox" is in module units, while "width" and "height" scale by "cellsize". PNG output ---------- For raster output, use "save()" to write a PNG file or "get_imagedata()" to receive the raw PNG bytes. PDF417Encoder("WDBCA45D2HA327260").save("pdf417.png") EPS output ---------- For embedding in LaTeX ("\includegraphics") or other vector print workflows, use "save_eps()" (or "get_eps()" to receive the EPS as a string). PDF417Encoder("WDBCA45D2HA327260").save_eps("pdf417.eps") The "cellsize" argument is the side length of one module in PostScript points (1 point = 1/72 inch). Terminal output --------------- For quick on-screen display, "get_terminal_art()" returns an on-screen rendering using horizontal half-block characters ("▌"/"▐"): one codeword row per line, two modules per character. That keeps codeword- row boundaries aligned with character boundaries and gives an on- screen aspect ratio close to the spec's "Y >= 3X". print(PDF417Encoder("WDBCA45D2HA327260").get_terminal_art()) ████▐▐▐ ▐▌▌▌ █ █▐▐▌█▌ ▐█▐▐▐█ ███▌▌ ▌▌▐ ████▐▐▐ ▐██▌▌▌ █▌▌█▌ ▐▐██▐██▐▐ ▐▌ ███▌▌ ▌▌▐ ████▐▐▐ ▐▌▌▌██▌ ██▌▌██▐▌▐█▐▐ ▐██▌███▌▌ ▌▌▐ ████▐▐▐ ▐█▌▌▐▐█▌ █▐▐█ ▐█▌▐▌▌██▐██ ███▌▌ ▌▌▐ ████▐▐▐ ▐█▐▐█ █ ██▌▌ ▌▐▌▐▌▌█▌ ▐ ███▌▌ ▌▌▐ ████▐▐▐ ▐██▐▐█ ▌▌ ██ ▐▌█▐█▐▐██▐ ███▌▌ ▌▌▐ ████▐▐▐ ▐▌▌▐█ ▐█▌▌ ▐▌█▌ █▐▐ █▌█▌ ███▌▌ ▌▌▐ ████▐▐▐ ▐██▐ ▌█ █ ▐ ▐█▐▐█▌▌▐▐ ███▌▌ ▌▌▐ ████▐▐▐ ▐▐ █▐██ ██▌▌█▌ ▌▐▐ █ ██▌███▌▌ ▌▌▐ ████▐▐▐ ▐▐ ▐█ █▌ ▌▌ █ █ ▐▐ ▐▌ █ ███▌▌ ▌▌▐ ████▐▐▐ ▐█▐ █▌ █ ███ ▌▌█▌▐▌▌▐█ ▐ ███▌▌ ▌▌▐ ████▐▐▐ ▐▌▌ ▌▐██ ▌█▐ █▌▐▌▌ █▐██▌███▌▌ ▌▌▐ ████▐▐▐ ▐█▐ ▐▐█ ▌▌▐█ ▐█ ▐▐ █ █ ███▌▌ ▌▌▐ ████▐▐▐ ▐██▐ ▐ ▐▌██▌ █▐▐ ▐█▌▌ ▌ ▌ ███▌▌ ▌▌▐ ████▐▐▐ ▐█▌▌ ▐█▌▌▌██ █▌█▌▐▐ ▌██ ███▌▌ ▌▌▐ ████▐▐▐ ▐█ ▌█▐█▌ ▌▐▐▌ ▐▌ ▐█ ▌▌ █▌ ███▌▌ ▌▌▐ ████▐▐▐ ▐▐ ▐██▐▌ ██▌▐▐▌▐█▐▐ ▐█▌ ▌███▌▌ ▌▌▐ ████▐▐▐ ▐██▌▐▐█▐▌▌ ██ ▐▐▐█▌▐▐█▌▐ ███▌▌ ▌▌▐ ████▐▐▐ ▐█▐▌▌ ▐▌ ▌▐▐▌ ▐▌ ▐█▌█▐ █▌ ███▌▌ ▌▌▐ ████▐▐▐ ▐▐ ██▌▐▌▌█▐██ ▐ ▐▐ ██▌█ ███▌▌ ▌▌▐ By default the output is wrapped in ANSI escape codes that force a white background and black foreground, so the symbol scans regardless of the terminal's colour scheme. Pass "ansi_bg=False" for plain output (correct only on a light-themed terminal). DXF (CAD) output ---------------- For direct part marking applications, "get_dxf()" returns a DXF representation of the symbol that CAD and CAM tools can read directly. The "cellsize" is in your chosen "units" (default ""mm"") rather than pixels. encoder = PDF417Encoder("WDBCA45D2HA327260") with open("part.dxf", "w") as f: f.write(encoder.get_dxf(cellsize=0.5, units="mm")) The default "inverse=True" emits geometry for the light modules, including the quiet zone -- so the bounding box frames the symbol. Pass "inverse=False" to emit only the dark modules instead, matching the symbol's normal appearance; the bounding box then hugs the dark cells and the quiet zone has to be reintroduced downstream. Symbol shape ============ PDF417 symbols are rectangular: "rows" (3-90) and "columns" (1-30, counting data columns only -- the start/stop patterns and row indicators are added on top). pyStrich picks both automatically to give a roughly square aspect at the default "row_height=3". For applications that need a fixed width -- for example a label of known size -- pin "columns" and let pyStrich choose the row count: PDF417Encoder("WDBCA45D2HA327260", columns=4).save("pdf417-4col.png") Increasing "columns" makes the symbol wider and shorter; decreasing it narrower and taller. The encoder raises "PyStrichInvalidInput" if the data does not fit at the chosen column count. Error correction level ====================== PDF417 embeds redundant data so that a partly-damaged symbol can still be read. The error correction level (ECL) is an integer 0-8; each level doubles the number of error-correction codewords: +---------+--------------+-----------------------------------------------+ | ECL | EC codewords | Typical use | |=========|==============|===============================================| | "0" | 2 | Verification only, no recovery. | +---------+--------------+-----------------------------------------------+ | "1" | 4 | Tiny symbols where space is at a premium. | +---------+--------------+-----------------------------------------------+ | "2" | 8 | **Default** for up to 40 data codewords. | +---------+--------------+-----------------------------------------------+ | "3" | 16 | Default for 41-160 data codewords. | +---------+--------------+-----------------------------------------------+ | "4" | 32 | Default for 161-320 data codewords. | +---------+--------------+-----------------------------------------------+ | "5" | 64 | Default for 321-863 data codewords. | +---------+--------------+-----------------------------------------------+ | "6" | 128 | Hostile environments (label damage likely). | +---------+--------------+-----------------------------------------------+ | "7" | 256 | As above; large symbols only. | +---------+--------------+-----------------------------------------------+ | "8" | 512 | As above; only fits up to 415 data codewords. | +---------+--------------+-----------------------------------------------+ The defaults follow the spec's minimum-level recommendation. Pick a higher level for symbols likely to be partly obscured -- scratched, smudged or torn -- and the symbol will grow to fit the extra correction. PDF417Encoder("WDBCA45D2HA327260", ecl=5).save("pdf417-high.png") Non-ASCII text ============== "PDF417Encoder" accepts any Unicode string directly and picks the narrowest character set that fits. Wrap the input in "PDF417Data" only when you want to constrain that choice -- for example, to enforce ""ascii"" so a stray non-ASCII character raises instead of silently growing the symbol: +------------------+---------------------------------------------------------------------+ | Encoding | Behaviour | |==================|=====================================================================| | ""ascii"" | Raises "PyStrichInvalidInput" on any byte > 127. | +------------------+---------------------------------------------------------------------+ | ""iso-8859-1"" | Latin-1 -- the PDF417 default character interpretation. No ECI | | | prefix; conformant decoders pick it up automatically. | +------------------+---------------------------------------------------------------------+ | ""utf-8"" | Emits codeword 927 + 26 (ECI 000026) once at the start of the | | | symbol and byte-encodes the input. Conformant decoders pick up the | | | encoding automatically. | +------------------+---------------------------------------------------------------------+ Tip: The auto-selected encoding is always the narrowest one that fits, so passing a plain "str" already gives you the smallest symbol. Picking an encoding by hand is mostly useful for input validation -- e.g. reject anything outside ASCII at the boundary. # Plain str: Latin-1 picked automatically, no ECI prefix. PDF417Encoder("Ich dachte, Sie wären kräftiger").save("latin1.png") [image: PDF417 encoding "Ich dachte, Sie wären kräftiger" as Latin-1.][image] # Plain str: UTF-8 picked automatically, ECI 26 emitted. PDF417Encoder("€5 親切にしろ 🐻‍❄️").save("utf8.png") [image: PDF417 encoding "€5 親切にしろ 🐻‍❄️" as UTF-8 (ECI 26).][image] If you pin an encoding that does not fit the input, the raised error names the offending character and suggests the encoding that *would* have worked: >>> from pystrich.pdf417 import PDF417Data >>> PDF417Data("Ich dachte, Sie wären kräftiger", encoding="ascii") Traceback (most recent call last): ... pystrich.exceptions.PyStrichInvalidInput: PDF417Data encoding 'ascii' expects ASCII; got 'ä'. Try PDF417Data('Ich dachte, Sie wären kräftiger', encoding='iso-8859-1') or pass auto_encoding=True to select an encoding automatically. API === class PDF417Encoder(text: PDF417Data | str, *, ecl: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8] | None = None, columns: int | None = None, quiet_zone: int = 2, row_height: int = 3) Bases: "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. Variables: * **matrix** -- 0/1 module grid built from the codeword stream. * **rows** -- Number of codeword rows in the symbol. * **columns** -- Number of data columns (excluding row indicators). * **ecl** -- Error correction level in effect. * **row_height** -- Matrix rows per codeword row (default 3). * **quiet_zone** -- White border applied by the renderer, in modules. get_ascii() -> str Return an ASCII-art rendering of the symbol. Return type: str get_dxf(cellsize: float = 1.0, inverse: bool = True, units: Literal['in', 'ft', 'mi', 'mm', 'cm', 'm'] | None = 'mm', *, mark_shape: MarkShape = MarkShape.SQUARE_CELLS) -> str Return a DXF (CAD) representation of the symbol. Parameters: * **cellsize** -- Side length of one module in "units". * **inverse** -- If "True" (the default), light modules are drawn as filled cells. If "False", dark modules are drawn, matching the normal appearance of the symbol. * **units** -- One of ""in"", ""ft"", ""mi"", ""mm"", ""cm"" or ""m"", or "None" for Unspecified ("$INSUNITS=0"). * **mark_shape** -- How matched cells are grouped and drawn. Return type: str Added in version 0.9. Changed in version 0.12: "units" now supports ""in"", ""ft"", ""mi"", ""cm"", ""m"" and "None" (Unspecified); previously any value other than ""mm"" was silently treated as unspecified. get_eps(cellsize: int = 5, *, inverse: bool = False, mark_shape: MarkShape = MarkShape.HORIZONTAL_RUNS) -> str Render the symbol and return EPS markup. Parameters: * **cellsize** -- Side length in PostScript points of one module. * **inverse** -- If "True", mark the light cells instead of the dark ones. * **mark_shape** -- How matched cells are grouped and drawn. Return type: str Added in version 0.12. get_imagedata(cellsize: int = 5) -> bytes Render the symbol and return PNG bytes. Parameters: **cellsize** -- Side length in pixels of one module. Returns: PNG-encoded image data. Return type: bytes get_pilimage(cellsize: int = 5) -> PIL.Image.Image Render the symbol and return a Pillow image. Parameters: **cellsize** -- Side length in pixels of one module. Returns: The rendered symbol. Return type: PIL.Image.Image Added in version 0.11. get_svg(cellsize: int = 5, *, inverse: bool = False, mark_shape: MarkShape = MarkShape.HORIZONTAL_RUNS) -> str Render the symbol and return SVG markup. Parameters: * **cellsize** -- Side length in user units of one module. * **inverse** -- If "True", mark the light cells instead of the dark ones. * **mark_shape** -- How matched cells are grouped and drawn. Return type: str Added in version 0.12. get_terminal_art(*, ansi_bg: bool = True) -> str Render the symbol using Unicode half-block characters for terminals. Each character represents two matrix rows and one column, producing approximately square cells in a typical fixed-width font and yielding a result that is scannable on screen. Parameters: **ansi_bg** -- If "True" (the default), wrap each line in ANSI escape codes that force a white background and black foreground, making the symbol scannable regardless of the terminal's colour scheme. Set to "False" for plain output (correct only on a light-themed terminal). Return type: str Added in version 0.12. save(filename: str | PathLike[str], cellsize: int = 5) -> None Save the symbol as a PNG. Pass a ".png" filename. Parameters: * **filename** -- PNG output path. * **cellsize** -- Side length in pixels of one module. save_eps(filename: str | PathLike[str], cellsize: int = 5, *, inverse: bool = False, mark_shape: MarkShape = MarkShape.HORIZONTAL_RUNS) -> None Save the symbol as an EPS file. Pass an ".eps" filename. Parameters: * **filename** -- EPS output path. * **cellsize** -- Side length in PostScript points of one module. * **inverse** -- If "True", mark the light cells instead of the dark ones. * **mark_shape** -- How matched cells are grouped and drawn. Added in version 0.12. save_svg(filename: str | PathLike[str], cellsize: int = 5, *, inverse: bool = False, mark_shape: MarkShape = MarkShape.HORIZONTAL_RUNS) -> None Save the symbol as an SVG file. Pass a ".svg" filename. Parameters: * **filename** -- SVG output path. * **cellsize** -- Side length in user units of one module. * **inverse** -- If "True", mark the light cells instead of the dark ones. * **mark_shape** -- How matched cells are grouped and drawn. Added in version 0.12. init_renderer() -> PDF417Renderer Construct a "PDF417Renderer" for the encoded matrix. Wraps the matrix in a quiet zone before handing it back and updates "width" and "height" to the renderer's module dimensions. class PDF417Data(*segments: str, encoding: Literal['ascii', 'iso-8859-1', 'utf-8'] | None = None, auto_encoding: bool = False) Bases: "object" Encoder input with an explicit character-set choice. "PDF417Encoder" accepts a plain "str" and selects the encoding automatically. Use "PDF417Data" only to pin the encoding -- for example, force ""ascii"" to reject non-ASCII input. Pass either "encoding=" (one of ""ascii"", ""iso-8859-1"", ""utf-8"") or "auto_encoding=True". With "auto_encoding=True" the constructor picks the narrowest fitting encoding; any "encoding=" argument is then ignored. Variables: * **segments** -- Tuple of input string segments. * **encoding** -- The chosen Python codec name. * **auto_encoding** -- "True" if the encoding was picked automatically.