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")
PDF417 encoding "WDBCA45D2HA327260".

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)
PDF417 encoding "WDBCA45D2HA327260" rendered with 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

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")
PDF417 encoding "Ich dachte, Sie wären kräftiger" as Latin-1.
# Plain str: UTF-8 picked automatically, ECI 26 emitted.
PDF417Encoder("€5 親切にしろ 🐻‍❄️").save("utf8.png")
PDF417 encoding "€5 親切にしろ 🐻‍❄️" as UTF-8 (ECI 26).

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 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[source]

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_encodingTrue if the encoding was picked automatically.