Pro edition
Legal — Deep Reference
At a glance
Section titled “At a glance”- Generates sequential Bates number stamps as per-page PDF content stream fragments.
- Three public types:
BatesNumberConfig(immutable configuration),BatesNumberer(engine),BatesPosition(six-case position enum). - Each fragment is self-contained. Graphics state is saved and restored, so appending never disturbs existing page content.
- Output is deterministic: a fragment is a pure function of configuration, stamp text, and page size.
- The module raises no exceptions. Out-of-range inputs degrade by the documented fallback rules.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Pro (nextpdf/pro) and activates with a Pro-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
No per-feature license flag exists. This is a Pro-edition capability.
composer require nextpdf/pro:^3Public API surface
Section titled “Public API surface”| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
BatesNumberConfig::__construct | string $prefix = '', string $suffix = '', int $startNumber = 1, int $padding = 5, BatesPosition $position = BatesPosition::BottomRight, float $fontSize = 9.0, string $fontFamily = 'Courier', float $opacity = 1.0, bool $useLayer = true, string $layerName = 'Bates Numbers', float $inset = 15.0 | Immutable appearance and numbering configuration | BatesNumberConfig | — | All eleven properties are public and readonly. |
BatesNumberConfig::formatNumber | int $pageIndex (0-based) | prefix + zero-padded (startNumber + pageIndex) + suffix | string | — | A number wider than padding is not truncated. |
BatesNumberConfig::getRange | int $pageCount | First and last formatted stamps for the run | array{first: string, last: string} | — | Assumes pageCount >= 1; a count of 0 formats page index -1. |
BatesNumberer::__construct | BatesNumberConfig $config | Binds the configuration | BatesNumberer | — | The class is final and readonly. |
BatesNumberer::generate | int $pageCount, array $pageSizes, string $prefix = '', int $startFrom = 1 | Static fast path with default appearance | list<string> | — | Suffix, position, font, opacity, and layer stay at their defaults. |
BatesNumberer::generateStreams | int $pageCount, list<array{width: float, height: float}> $pageSizes | One self-contained fragment per page | list<string> | Never throws; a missing size entry falls back to A4 portrait | Fragment count equals pageCount; extra size entries are ignored. |
BatesNumberer::buildPageStream | string $text, float $pageWidth, float $pageHeight | Builds one page’s stamp fragment | string | — | q/Q wrapped; stamp text escaped for literal-string syntax. |
BatesNumberer::getConfig | — | Returns the bound configuration | BatesNumberConfig | — | — |
BatesPosition | enum cases BottomLeft, BottomCenter, BottomRight, TopLeft, TopCenter, TopRight | String-backed position vocabulary | — | — | Backing values are kebab-case (for example bottom-right). |
BatesPosition::coordinates | float $pageWidth, float $pageHeight, float $textWidth, float $inset = 15.0 | X/Y for the stamp baseline in PDF-native space | array{x: float, y: float} | — | Origin is bottom-left; top rows place the baseline inset from the top edge. |
Entry-point signatures
Section titled “Entry-point signatures”public function __construct( public string $prefix = '', public string $suffix = '', public int $startNumber = 1, public int $padding = 5, public BatesPosition $position = BatesPosition::BottomRight, public float $fontSize = 9.0, public string $fontFamily = 'Courier', public float $opacity = 1.0, public bool $useLayer = true, public string $layerName = 'Bates Numbers', public float $inset = 15.0,) {}public static function generate( int $pageCount, array $pageSizes, string $prefix = '', int $startFrom = 1,): arraypublic function generateStreams(int $pageCount, array $pageSizes): arraypublic function buildPageStream(string $text, float $pageWidth, float $pageHeight): stringpublic function coordinates( float $pageWidth, float $pageHeight, float $textWidth, float $inset = 15.0,): arrayBehavior contract
Section titled “Behavior contract”Numbering
Section titled “Numbering”BatesNumberConfig::formatNumber computes startNumber + pageIndex, left-pads the number with zeros to padding digits, and wraps it with prefix and suffix. getRange returns the first and last formatted stamps for a page count. Use it to chain continuation numbering across productions.
Fragment anatomy
Section titled “Fragment anatomy”Each fragment is, in order: a graphics-state save (q), a fill-color operator, an optional marked-content begin, a text block that positions and shows the stamp, an optional marked-content end, and a restore (Q). Coordinates and the font size are serialized with six decimal places, so identical inputs produce identical bytes. The stamp text escapes \, (, and ) before it enters the literal string.
Font binding
Section titled “Font binding”The text block selects the fixed font resource name /BatesFont. The embedding page’s resource dictionary must map that name to a font matching the configured fontFamily, and the family must resolve in the font registry. Fragment generation itself never consults the registry.
Placement
Section titled “Placement”BatesPosition::coordinates computes the stamp baseline in PDF-native space; the origin is bottom-left. Center and right placement subtract an estimated text width: byte length times 0.6 times the font size, a monospace approximation. Proportional fonts and multi-byte text shift that estimate. Left placement does not depend on it.
Layers
Section titled “Layers”With useLayer enabled (the default), the fragment brackets the text between BDC and EMC marked-content operators. The marked-content name has the form /Lyr_<name>, derived from layerName with non-word characters replaced by underscores. The bracketing is fragment-level only: registering the corresponding optional content group in the document — the step that makes the layer toggleable in a viewer — belongs to the embedding writer.
Opacity
Section titled “Opacity”An opacity below 1.0 is emitted as a lighter grayscale fill. A fully opaque stamp renders black.
The engine applies Bates numbering exactly as configured.
Edge cases & failure modes
Section titled “Edge cases & failure modes”generateStreamsnever throws on apageSizesmismatch. A missing entry falls back to A4 portrait,595.276by841.890points; extra entries are ignored.- Fragment count always equals
pageCount. - A number wider than
paddingis not truncated; the stamp text simply grows. getRangeassumespageCount >= 1. A count of 0 formats page index -1, that isstartNumber - 1.- Opacity is a grayscale lightening, not ExtGState transparency; overlapped content beneath the stamp is not blended.
- Stamp bytes other than
\,(, and)pass through unencoded. Encoding correctness for non-ASCII text depends on the bound font. - Bates marks are overlay content. They do not redact, remove, or encrypt anything on the page.
- The module performs no cryptographic operations; FIPS mode does not change its behavior.
Conformance
Section titled “Conformance”| Behavior | Reference | Status |
|---|---|---|
Layer bracketing via BDC/EMC marked-content operators | ISO 32000-2:2020 §8.11.3.2 | Partial — the fragment emits the bracketing; optional content group registration is the embedding writer’s step |
These rows record the specification the module is built against.
Development notes
Section titled “Development notes”- Fragments are pure string values. Test them by direct byte comparison; no document context is required.
buildPageStreamis public and unit-testable in isolation: pass pre-formatted text and explicit page dimensions.- For continuation numbering across productions, seed
startNumberfrom the previous run and record thegetRangeoutput in your production log. - Layer names are sanitized to word characters. Prefer ASCII layer names so the marked-content name stays readable in inspection tools.
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.