Config: immutable document configuration
At a glance
Section titled “At a glance”Config is an immutable document configuration object. When you change a
setting, a typed wither returns a new instance, so you can safely share
configuration across documents and workers. NextPdfConfig is a separate
static class that stores process-wide Cascading Style Sheets (CSS) parser
safety limits.
Install
Section titled “Install”composer require nextpdf/core:^3Both classes are part of the core package. Config has existed since 1.7.0.
NextPdfConfig has existed since 2.2.0.
Conceptual overview
Section titled “Conceptual overview”Config is a final readonly class marked #[DisallowDynamicProperties]. It
stores only scalars and final readonly value objects, so the full graph is
deeply immutable. It does not provide a generic with(string, mixed) setter.
Each mutation uses a dedicated typed wither. There are 19 of them:
withPageSize(), withMargins(), withCompress(), through
withOutputColorProfile(), and each reconstructs the object with named
arguments.
Named-argument reconstruction is deliberate. When you add a constructor
parameter, an existing wither never silently shifts a positional argument. A
compatibility test uses reflection to pin the public method list and parameter
count. Signature drift fails continuous integration (CI).
The constructor exposes the configuration surface with safe defaults:
| Parameter | Type | Default |
|---|---|---|
pageSize | PageSize | PageSize(595.276, 841.890, 'A4') |
margins | Margin | Margin(10.0, 10.0, 10.0, 10.0) |
compress | bool | true |
autoPageBreak | bool | true |
breakMargin | float | 20.0 |
lang | string (BCP 47) | '' |
fontsDirectory | string | '' |
imageCacheBytes | int | 52_428_800 |
deterministic | ?DeterministicSettings | null |
cryptoPolicy | ?CryptoPolicyInterface | null |
branding | ?BrandingConfig | null |
degradationPolicy | DegradationPolicy | Balanced |
degradationOverrides | array<string, DegradationPolicy> | [] |
cssRenderingMode | CssRenderingMode | Normal |
auditCollector | ?AuditCollector | null |
cssFeatureFlags | ?CssFeatureFlags | null |
cssLayoutMode | CssLayoutMode | Streaming |
retainedNodeBudget | int | 50_000 |
layoutTelemetryCollector | ?LayoutTelemetryCollector | null |
telemetryEnabled | bool | false |
outputColorProfile | OutputColorProfile | DeviceRGB |
Three settings enforce value bounds. retainedNodeBudget accepts the closed
range [5_000, 100_000] (constants RETAINED_NODE_BUDGET_MIN,
RETAINED_NODE_BUDGET_MAX, default RETAINED_NODE_BUDGET_DEFAULT).
withRetainedNodeBudget() throws InvalidArgumentException outside that range.
effectiveRetainedNodeBudget() returns 0 in streaming mode. Otherwise it
clamps the value back into range as a defense-in-depth check against direct
positional construction. validate() enforces cross-field invariants the
per-field withers cannot see on their own. CssLayoutMode::Auto is reserved
and raises NotImplementedException. CssRenderingMode::Safe with
CssLayoutMode::Retained raises IncompatibleRenderingModeException.
auditCollector is the caller-supplied audit report collector. It is used only
when cssRenderingMode is CssRenderingMode::Audit, where it drives a private
nextpdfAudit Extensible Metadata Platform (XMP) block. Other rendering modes
ignore it.
NextPdfConfig serves a separate concern. It is a non-instantiable static
utility that holds process-wide CSS parser limits. The limits are maximum
stylesheet bytes (default 512 KB) and maximum CSS nesting depth (default 8).
These are security boundaries against resource exhaustion from hostile input,
not per-document preferences. They are static for that reason. The setters
clamp to a floor
(max(1024, …) bytes, max(1, …) depth). resetDefaults() is marked
@internal for test use only.
API surface
Section titled “API surface”| Symbol | Kind | Key members |
|---|---|---|
NextPDF\Core\Config | final readonly class | 21 constructor parameters; 19 typed withers; validate(), isSafeMode(), isRetainedMode(), effectiveRetainedNodeBudget(), resolveFeatureFlags() |
NextPDF\Core\Config::RETAINED_NODE_BUDGET_MIN | const int | 5_000 |
NextPDF\Core\Config::RETAINED_NODE_BUDGET_MAX | const int | 100_000 |
NextPDF\Core\Config::RETAINED_NODE_BUDGET_DEFAULT | const int | 50_000 |
NextPDF\Core\NextPdfConfig | final class | setMaxCssBytes(), getMaxCssBytes(), setMaxCssNestingDepth(), getMaxCssNestingDepth(), resetDefaults() (@internal) |
Withers: withPageSize, withMargins, withCompress, withAutoPageBreak,
withLang, withFontsDirectory, withImageCacheBytes, withDeterministic,
withCryptoPolicy, withBranding, withDegradationPolicy,
withDegradationOverride, withCssRenderingMode, withCssFeatureFlags,
withCssLayoutMode, withRetainedNodeBudget, withLayoutTelemetryCollector,
withTelemetryEnabled, withOutputColorProfile.
Code sample — Quick start
Section titled “Code sample — Quick start”Start with defaults and adjust two settings.
<?php
declare(strict_types=1);
use NextPDF\Core\Config;use NextPDF\ValueObjects\Margin;use NextPDF\ValueObjects\PageSize;
$config = (new Config()) ->withPageSize(PageSize::Letter()) ->withMargins(Margin::uniform(18.0));
// Each wither returns a new instance; the original is unchanged.Code sample — Production
Section titled “Code sample — Production”Compose a deterministic, validated configuration, and tighten the process-wide CSS limits for untrusted input.
<?php
declare(strict_types=1);
use NextPDF\Core\Config;use NextPDF\Core\CssRenderingMode;use NextPDF\Core\NextPdfConfig;use NextPDF\ValueObjects\Margin;use NextPDF\ValueObjects\PageSize;
$config = (new Config()) ->withPageSize(PageSize::A4()) ->withMargins(Margin::symmetric(vertical: 20.0, horizontal: 15.0)) ->withCompress(true) ->withCssRenderingMode(CssRenderingMode::Safe);
// Reject mutually exclusive modes before generation starts.$config->validate();
// Process-wide hardening for hostile stylesheet input.NextPdfConfig::setMaxCssBytes(262_144); // 256 KBNextPdfConfig::setMaxCssNestingDepth(4);Edge cases & gotchas
Section titled “Edge cases & gotchas”Configenforces no cross-field rule until you callvalidate(). A wither cannot detect aSafe+Retainedconflict by itself; callvalidate()before generation.withRetainedNodeBudget()is the only wither that throws on its own input (InvalidArgumentExceptionoutside[5_000, 100_000]).effectiveRetainedNodeBudget()returns0in streaming mode. That signals “the Tier-1 budget does not apply”, not “no nodes allowed”.withLayoutTelemetryCollector()andwithTelemetryEnabled()are independent. Wiring a collector without enabling telemetry stages it for a later canary. Enabling telemetry without a collector is valid and has no effect.NextPdfConfigis process-wide and static. A change affects every document in the process until reset. It is a security boundary, not a per-document preference. Keep it out of per-request mutation paths.NextPdfConfig::resetDefaults()is@internal. Do not call it in production code.
Performance
Section titled “Performance”A wither allocates one new Config and copies references to existing value
objects. The operation is O(1) in the number of settings and performs no deep
copy, because every held value is immutable. NextPdfConfig accessors are
static field reads, O(1). The default performance_budget for this reference
page is wall_ms: 1500, peak_mb: 64.
Security notes
Section titled “Security notes”NextPdfConfig bounds parser resource use. The default 512 KB stylesheet cap
and depth-8 nesting cap protect against denial-of-service from pathologically
large or deeply nested CSS. Lower both limits when the stylesheet source is
untrusted. Config::cryptoPolicy carries a CryptoPolicyInterface for
cryptographic policy enforcement, such as a Federal Information Processing
Standards (FIPS) profile. It is null by default and is set through
withCryptoPolicy(). Config::deterministic controls fixed timestamps and
identifiers for
byte-identical output and is required for reproducible builds.
Conformance
Section titled “Conformance”This module is engine configuration and carries no normative standard
citation. Settings that drive standards behavior, including
outputColorProfile (OutputIntent emission), cryptoPolicy (FIPS), and
deterministic (reproducible builds), are documented against their clauses in
the modules that emit them.
See also
Section titled “See also”/modules/core/document/— the consumer ofConfig/modules/core/valueobjects/—PageSize,Marginused byConfig/modules/core/contracts/—CryptoPolicyInterface,DegradationPolicy/modules/core/event/—Configcarried onDocumentCreatedEvent/modules/core/exception/—InvalidConfigException,NotImplementedException
Glossary: typed wither · degradation policy · value object