-
Notifications
You must be signed in to change notification settings - Fork 0
Internals Forms and Outlines
This page covers the internal representation of outline trees (bookmarks), link annotations, and AcroForm interactive fields in phppdf.
The outline is a hierarchical doubly-linked tree of /Type /Outlines dictionaries with /First, /Last, /Next, /Prev, /Parent, and /Count pointers. phppdf builds this tree from the Outline::add(...) declarative API and serializes the cross-references at output() time.
Each outline item has a /Dest referencing a destination:
-
[page /XYZ left top zoom]- page + position + zoom (the default forDestination::page(0)is XYZ at top-left). -
[page /Fit]- fit whole page in viewport. -
[page /FitH top]- fit page width.
Each $page->link(...) call adds a /Type /Annot /Subtype /Link dictionary to the page's /Annots array. Two variants:
- URL link:
/A << /S /URI /URI (https://...) >>. - Internal jump:
/Dest [page ...]using the same destination encoding as outline items.
The /Border [0 0 0] entry makes the link rectangle invisible - the standard "underline appears on click in Acrobat" behavior is what users expect, but the rectangle itself is not outlined by default.
Pages without any link() calls produce no /Annots array, so their content streams remain byte-identical to the equivalent page from earlier phases of the library. This is what allows the golden-test fixtures from Phase 1-6 to stay valid after Phase 7 shipped outlines.
AcroForm fields live both in the document catalog (as /AcroForm) and on individual pages (as widget /Annots). phppdf uses a hybrid appearance strategy:
- The document catalog's
/AcroFormdictionary gets/NeedAppearances true. - The field declares a
/DA(default appearance) string like/Helv 12 Tf 0 g, which sets the font and color. - No
/AP(appearance stream) is generated. The PDF reader is responsible for rendering the field on the fly when the file opens. - This way, the displayed text always matches the field's current value (including user-edited values).
- For these,
NeedAppearancesis unreliable across readers (Acrobat draws boxes but Firefox PDF.js does not, etc.). - phppdf pre-generates the
/AP(appearance) streams for bothOnandOffstates, embedding a small content stream that draws the tick mark or filled circle. - The widget references these via
/AP << /N << /Yes 12 0 R /Off 13 0 R >> >>. - This guarantees identical rendering across Acrobat, browser viewers (Chrome PDF, Firefox PDF.js), headless PDF renderers, and others.
Optional per-field FieldAppearance:
- Border color + width: emitted as
/Border [0 0 W]plus/BS << /W ... /S /S >>and/MK << /BC [r g b] >>. The/Borderentry is what makes Edge / Firefox / Brave render the colored frame (some readers ignore/BSand only honor/Border, so we emit both). - Background color:
/MK << /BG [r g b] >>. - Text color: encoded in the
/DAstring (r g b rgfor fill). - Font: Helvetica, Courier, or Times. Auto-registered in the form's default resources
/AcroForm /DR << /Font << /Helv 14 0 R /Cour 15 0 R /TiRo 16 0 R >> >>only if used. - Font size: encoded in
/DA(/Helv 12 Tf). - Alignment (TextField only):
/Q 0(left),/Q 1(center),/Q 2(right).
Pages without any field() calls produce:
- No extra
/Annotsentries (only whatlink()adds, if anything). - No
/AcroFormentry in the catalog.
So the byte-identity baseline of earlier phases is preserved.
MIT licensed. Source on GitHub - if phppdf helps you, you can buy me a coffee.