Skip to content

ValueObjects: domain primitives + units

The ValueObjects module provides the immutable geometry primitives used across the engine: PageSize, Dimension, Position, Margin, and the Unit enum. Each one is a final readonly class. You can share an instance freely, and each transformation returns a new instance.

Terminal window
composer require nextpdf/core:^3

All four value objects are final readonly. They store float coordinates and expose no mutators; transformation methods rebuild a new instance with named arguments. You can cache them, pass them between documents, and share them across workers without defensive copying.

PageSize stores width, height (in points; 1 pt = 1/72 inch), and a name. Static factories cover the International Organization for Standardization (ISO) 216 A series (A0A6), the ISO 216 B series (B0B5), and North American sizes (Letter, Legal, Tabloid). The A/B dimensions follow the trimmed paper-size series defined by ISO 216. fromName() resolves names without case sensitivity and throws PageLayoutException for an unknown name. landscape() and portrait() return the orientation variant, or self when the page already has that orientation. toDimension() converts the page size to a Dimension in points.

Dimension stores width, height, and a Unit. Factories (fromMillimeters(), fromPoints(), fromInches()) create a dimension in the selected unit. toPoints() and toMillimeters() convert units; toPoints() returns self when the dimension is already in points. withWidth() and withHeight() return a resized copy. Conversion uses exact factors: 72/25.4 points per millimeter, 72/2.54 per centimeter, and 72 per inch.

Position is a 2D point in Portable Document Format (PDF) user space (x, y). origin() returns (0, 0). translate(dx, dy) returns an offset copy. withX() / withY() return a copy with one axis replaced.

Margin holds top, right, bottom, and left. Factories are uniform() (same value on all sides), symmetric(vertical, horizontal), and zero().

Unit is a string-backed enum: Point (pt), Millimeter (mm), Centimeter (cm), and Inch (in). toPointFactor() returns the multiplier to points.

SymbolKindKey members
NextPDF\ValueObjects\PageSizefinal readonly class$width, $height, $name; factories A0A6, B0B5, Letter, Legal, Tabloid; fromName(), landscape(), portrait(), toDimension()
NextPDF\ValueObjects\Dimensionfinal readonly class$width, $height, $unit; fromMillimeters(), fromPoints(), fromInches(), toPoints(), toMillimeters(), withWidth(), withHeight()
NextPDF\ValueObjects\Positionfinal readonly class$x, $y; origin(), translate(), withX(), withY()
NextPDF\ValueObjects\Marginfinal readonly class$top, $right, $bottom, $left; uniform(), symmetric(), zero()
NextPDF\ValueObjects\Unitstring enumPoint, Millimeter, Centimeter, Inch; toPointFactor()

Use factories to construct geometry primitives.

<?php
declare(strict_types=1);
use NextPDF\ValueObjects\Margin;
use NextPDF\ValueObjects\PageSize;
use NextPDF\ValueObjects\Position;
$page = PageSize::A4(); // 595.276 x 841.890 pt
$margin = Margin::uniform(18.0); // 18 pt all sides
$origin = Position::origin()->translate(72.0, 72.0); // 1 inch in from corner

Convert units, derive orientation, and pass margins into a configuration.

<?php
declare(strict_types=1);
use NextPDF\Core\Config;
use NextPDF\ValueObjects\Dimension;
use NextPDF\ValueObjects\Margin;
use NextPDF\ValueObjects\PageSize;
// A label sized in millimeters, resolved to points for the engine.
$label = Dimension::fromMillimeters(width: 100.0, height: 150.0)->toPoints();
$page = PageSize::A4()->landscape(); // swap to width >= height
$margin = Margin::symmetric(vertical: 20.0, horizontal: 15.0);
$config = (new Config())
->withPageSize($page)
->withMargins($margin);
// $label->width / $label->height are now in points for downstream layout.
  • All dimensions use points unless you provide a Dimension with an explicit Unit. Config margins use points by default.
  • PageSize::landscape() / portrait() return the same instance when the orientation already matches; no allocation occurs, and identity is preserved.
  • PageSize::fromName() ignores case but resolves only the named factories. An unknown name throws PageLayoutException, not a default size.
  • Dimension::toPoints() returns self when the unit is already Point; do not assume a new object.
  • These objects hold raw float values and apply no range validation. They accept negative or zero dimensions at construction. The layout and writer layers enforce geometry validity, not these objects.
  • Floating-point conversion uses exact rational factors (72/25.4, 72/2.54); round only at the presentation boundary to keep reproducible output stable.

Each value object is a flat readonly struct of floats. Construction and each transformation are O(1) single allocations with no deep copy because there is no nested mutable state. You can share an instance across documents without extra cost. The default performance_budget for this reference page is wall_ms: 1500, peak_mb: 64.

These value objects perform no input/output (I/O), carry no user-supplied strings beyond a page-size name, and hold no external resource handles, so they present no direct attack surface. PageSize::fromName() rejects unknown input with an exception instead of falling back silently, so a malformed configuration fails explicitly rather than producing unexpected page geometry.

SpecClauseTopic
ISO 216:2007A / B seriesTrimmed paper-size dimensions for the A* / B* factories (paraphrased; ISO prose not quoted, no chunk pinned)

The A and B factory dimensions correspond to the ISO 216 trimmed sizes, expressed in points. North American sizes (Letter, Legal, Tabloid) are de facto industry sizes with no ISO basis. The ISO reference is paraphrased under the site citation policy. No verbatim chunk is pinned.

  • /modules/core/config/Config uses PageSize and Margin
  • /modules/core/layout/ — layout consumers for these primitives
  • /modules/core/graphics/Position in drawing coordinate space
  • /modules/core/contracts/Orientation with PageSize
  • /modules/core/exception/PageLayoutException from fromName()