Skip to content

Config: immutable document configuration

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.

Terminal window
composer require nextpdf/core:^3

Both classes are part of the core package. Config has existed since 1.7.0. NextPdfConfig has existed since 2.2.0.

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:

ParameterTypeDefault
pageSizePageSizePageSize(595.276, 841.890, 'A4')
marginsMarginMargin(10.0, 10.0, 10.0, 10.0)
compressbooltrue
autoPageBreakbooltrue
breakMarginfloat20.0
langstring (BCP 47)''
fontsDirectorystring''
imageCacheBytesint52_428_800
deterministic?DeterministicSettingsnull
cryptoPolicy?CryptoPolicyInterfacenull
branding?BrandingConfignull
degradationPolicyDegradationPolicyBalanced
degradationOverridesarray<string, DegradationPolicy>[]
cssRenderingModeCssRenderingModeNormal
auditCollector?AuditCollectornull
cssFeatureFlags?CssFeatureFlagsnull
cssLayoutModeCssLayoutModeStreaming
retainedNodeBudgetint50_000
layoutTelemetryCollector?LayoutTelemetryCollectornull
telemetryEnabledboolfalse
outputColorProfileOutputColorProfileDeviceRGB

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.

SymbolKindKey members
NextPDF\Core\Configfinal readonly class21 constructor parameters; 19 typed withers; validate(), isSafeMode(), isRetainedMode(), effectiveRetainedNodeBudget(), resolveFeatureFlags()
NextPDF\Core\Config::RETAINED_NODE_BUDGET_MINconst int5_000
NextPDF\Core\Config::RETAINED_NODE_BUDGET_MAXconst int100_000
NextPDF\Core\Config::RETAINED_NODE_BUDGET_DEFAULTconst int50_000
NextPDF\Core\NextPdfConfigfinal classsetMaxCssBytes(), 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.

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.

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 KB
NextPdfConfig::setMaxCssNestingDepth(4);
  • Config enforces no cross-field rule until you call validate(). A wither cannot detect a Safe + Retained conflict by itself; call validate() before generation.
  • withRetainedNodeBudget() is the only wither that throws on its own input (InvalidArgumentException outside [5_000, 100_000]).
  • effectiveRetainedNodeBudget() returns 0 in streaming mode. That signals “the Tier-1 budget does not apply”, not “no nodes allowed”.
  • withLayoutTelemetryCollector() and withTelemetryEnabled() 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.
  • NextPdfConfig is 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.

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.

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.

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.

  • /modules/core/document/ — the consumer of Config
  • /modules/core/valueobjects/PageSize, Margin used by Config
  • /modules/core/contracts/CryptoPolicyInterface, DegradationPolicy
  • /modules/core/event/Config carried on DocumentCreatedEvent
  • /modules/core/exception/InvalidConfigException, NotImplementedException

Glossary: typed wither · degradation policy · value object