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")
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)
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 |
|---|---|---|
|
2 |
Verification only, no recovery. |
|
4 |
Tiny symbols where space is at a premium. |
|
8 |
Default for up to 40 data codewords. |
|
16 |
Default for 41-160 data codewords. |
|
32 |
Default for 161-320 data codewords. |
|
64 |
Default for 321-863 data codewords. |
|
128 |
Hostile environments (label damage likely). |
|
256 |
As above; large symbols only. |
|
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 |
|---|---|
|
Raises |
|
Latin-1 – the PDF417 default character interpretation. No ECI prefix; conformant decoders pick it up automatically. |
|
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")
# Plain str: UTF-8 picked automatically, ECI 26 emitted.
PDF417Encoder("€5 親切にしろ 🐻❄️").save("utf8.png")
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)[source]¶
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
eclis 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_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. IfFalse, dark modules are drawn, matching the normal appearance of the symbol.units – One of
"in","ft","mi","mm","cm"or"m", orNonefor Unspecified ($INSUNITS=0).mark_shape – How matched cells are grouped and drawn.
- Return type:
Added in version 0.9.
Changed in version 0.12:
unitsnow supports"in","ft","mi","cm","m"andNone(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:
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:
- 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:
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:
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 toFalsefor plain output (correct only on a light-themed terminal).- Return type:
Added in version 0.12.
- save(filename: str | PathLike[str], cellsize: int = 5) None¶
Save the symbol as a PNG. Pass a
.pngfilename.- 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
.epsfilename.- 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
.svgfilename.- 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.
- class PDF417Data(*segments: str, encoding: Literal['ascii', 'iso-8859-1', 'utf-8'] | None = None, auto_encoding: bool = False)¶
Bases:
objectEncoder input with an explicit character-set choice.
PDF417Encoderaccepts a plainstrand selects the encoding automatically. UsePDF417Dataonly to pin the encoding – for example, force"ascii"to reject non-ASCII input.Pass either
encoding=(one of"ascii","iso-8859-1","utf-8") orauto_encoding=True. Withauto_encoding=Truethe constructor picks the narrowest fitting encoding; anyencoding=argument is then ignored.- Variables:
segments – Tuple of input string segments.
encoding – The chosen Python codec name.
auto_encoding –
Trueif the encoding was picked automatically.