View Source Pdf.Reader.Font.Widths (ExPDF v1.0.1)
Per-font glyph-width lookup for text advance computation.
Builds closures of type (binary() -> [non_neg_integer()]) that return, for
a binary of raw font bytes, a list of glyph-space advance widths (one per
glyph code encoded in the binary).
Simple fonts (Type1, TrueType)
Width lookup uses /Widths, /FirstChar, /LastChar from the font dict.
Out-of-range codes fall back to /MissingWidth from /FontDescriptor, or
0 if absent. (§ 9.6.2.1, § 9.6.4)
Standard-14 fallback
When /Widths is entirely absent — typical of Standard 14 Type 1 fonts
(Helvetica, Times-Roman, Courier, Symbol, ZapfDingbats and their styled
variants) — the spec (§ 9.6.2.2) requires the reader to use bundled AFM
metrics. We do not bundle AFMs (documented gap), so every glyph is
approximated as 500 units (~0.5 em), the rough average across the
Adobe Standard 14 AFM tables. This restores usable text positioning for
Standard-14-only PDFs (most government forms, RFCs, simple reports);
exact column alignment still requires AFM bundling.
Source: Adobe Font Metrics for the Core 14 — public domain via https://github.com/adobe-type-tools/Adobe-Core-14-Font-AFM-Files (avg glyph width across the 14 fonts ≈ 500 units).
CIDFonts (Type0 → DescendantFonts[0])
Width lookup uses /W (Form A and Form B entries) and /DW (default: 1000).
(§ 9.7.4.3)
Cache
Widths closures for fonts referenced via {:ref, n, g} are cached in
Document.cache under key {:font_widths, {n, g}}, mirroring the decoder
cache strategy of Pdf.Reader.Font.
Spec references
- PDF 1.7 § 9.4.4 — Text advance formula (tx per glyph)
- PDF 1.7 § 9.6.2.1 — Simple font /Widths, /FirstChar, /LastChar
- PDF 1.7 § 9.6.4 — Font descriptor /MissingWidth
- PDF 1.7 § 9.7.4.3 — CIDFont /W and /DW arrays
Summary
Functions
Build a widths closure for a font dict or indirect reference.
Build widths closures for all fonts in a page's resources map.
Parse a CIDFont /W array into a %{cid => width} map.
Types
@type widths_fn() :: (binary() -> [non_neg_integer()])
Functions
@spec build_widths_fn( map() | {:ref, pos_integer(), non_neg_integer()}, Pdf.Reader.Document.t() ) :: {:ok, widths_fn(), Pdf.Reader.Document.t()} | {:error, term()}
Build a widths closure for a font dict or indirect reference.
Returns {:ok, widths_fn, updated_doc}.
Spec: § 9.6.2.1 (simple fonts), § 9.7.4.3 (CIDFonts).
@spec build_widths_for_resources(map(), Pdf.Reader.Document.t()) :: {:ok, %{required(binary()) => widths_fn()}, Pdf.Reader.Document.t()} | {:error, term()}
Build widths closures for all fonts in a page's resources map.
Mirrors Pdf.Reader.Font.build_decoders_for_resources/2.
Returns {:ok, %{font_name => widths_fn}, updated_doc}.
When doc.recover_mode is true: on per-font widths failure, installs a
zero-width fallback (all glyphs advance 0) and continues. The font_skipped
event is already logged by build_decoders_for_resources; no duplicate event
is emitted here.
When doc.recover_mode is false: halts on first font widths failure
(unchanged strict behavior).
@spec parse_w_array([term()]) :: %{required(non_neg_integer()) => non_neg_integer()}
Parse a CIDFont /W array into a %{cid => width} map.
Supports:
- Form A:
c [w1 w2 …]— CIDc,c+1, … each get successive widths - Form B:
c1 c2 w— all CIDs fromc1toc2inclusive get widthw
Both forms may be interleaved in the same array. (§ 9.7.4.3)