stability: Experimental
CJK vertical writing support
At a glance
Section titled “At a glance”Opt-in preview. The vertical composer is default-off. When it is off, the engine renders horizontally exactly as before — byte-identical. Turn it on only for the documents that need real vertical lines, and validate the result.
The HTML renderer adds a real vertical line composer for the CSS writing modes
writing-mode: vertical-lr and writing-mode: vertical-rl. When the composer is
on, glyphs stack top to bottom with per-glyph placement taken from the font’s
real vertical metrics (the vhea and vmtx tables), as the PDF vertical-writing
model in ISO 32000-2 §9.7.5 describes. Both vertical block-flow directions are
supported.
Install
Section titled “Install”composer require nextpdf/core:^3The composer ships in the core package. The CssFeatureFlags::$layoutVerticalComposer
opt-in is @since 6.1.0. The engine version is unchanged; the feature is
additive and default-off.
Conceptual overview
Section titled “Conceptual overview”Vertical composition turns on only when both layoutVerticalLr and
layoutVerticalComposer are set. With them on, a vertical-lr or vertical-rl
run composes as a real vertical line: each glyph is placed by its vertical
advance from the font’s vhea/vmtx metrics, and glyphs that UAX #50 marks
upright are kept upright. vertical-lr lays columns left to right;
vertical-rl lays columns right to left.
This differs from the cmap-aware encoding facade documented in Set CJK text with cmap-aware encoding, which proves the encoding path but does not itself drive a vertical writing mode. This page documents the layout-side composer that the writing-mode opt-in enables.
Fail-closed boundary — when it composes, and what it does otherwise
Section titled “Fail-closed boundary — when it composes, and what it does otherwise”The composer is conservative by design. It composes a run vertically only when every glyph in the run is UAX #50 upright with real vertical metrics, there is no open link inside the run, and the run is a single column. When any of those does not hold — the flag is off, or a run cannot be composed faithfully — the engine falls back to horizontal layout and emits a mode-matching deferred diagnostic:
HTML_WRITING_MODE_LR_DEFERREDfor avertical-lrrun that could not compose.HTML_WRITING_MODE_RL_DEFERREDfor avertical-rlrun that could not compose.
Each diagnostic carries a reason, so a deferral is observable and explainable,
never a silent horizontal render of text the author asked to set vertically.
Documented boundaries (later slices)
Section titled “Documented boundaries (later slices)”These cases are out of scope for the current slice and are tracked for later work:
- Rotated (non-upright) glyphs inside a vertical run.
- Multi-column vertical wrapping.
- Vertical link rectangles (a link inside a vertical run defers the run).
API surface
Section titled “API surface”| Symbol | Location | Role |
|---|---|---|
CssFeatureFlags::$layoutVerticalComposer | src/Html/CssFeatureFlags.php | Opt-in flag for the vertical line composer (default false). |
CssFeatureFlags::$layoutVerticalLr | src/Html/CssFeatureFlags.php | Gate for vertical-lr; both must be on to compose. |
Config::withCssFeatureFlags(CssFeatureFlags $flags): self | src/Core/Config.php | Attaches the flag set to a document configuration. |
The deferred-diagnostic codes HTML_WRITING_MODE_LR_DEFERRED and
HTML_WRITING_MODE_RL_DEFERRED surface through the render result’s advisory
channel.
Code sample — Quick start
Section titled “Code sample — Quick start”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;use NextPDF\Core\Document;use NextPDF\Html\Css\CssFeatureFlags;
$config = (new Config())->withCssFeatureFlags(new CssFeatureFlags( layoutVerticalLr: true, layoutVerticalComposer: true,));
$doc = Document::createStandalone($config);$doc->addPage();$doc->writeHtml( '<div style="writing-mode: vertical-rl; font-family: NotoSerifJP;">' . '日本語の縦書き' . '</div>',);$doc->save(__DIR__ . '/vertical.pdf');A run that cannot compose faithfully renders horizontally and adds an
HTML_WRITING_MODE_RL_DEFERRED advisory with a reason. Inspect the advisory
channel before treating vertical output as final.
Code sample — Production
Section titled “Code sample — Production”Register a font that carries real vertical metrics through DocumentFactory, so
the composer can read vhea/vmtx, then opt the document into the composer.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Html\Css\CssFeatureFlags;use NextPDF\Typography\FontRegistry;
$fontRegistry = new FontRegistry();$fontRegistry->register('/path/to/NotoSerifJP-Regular.otf', alias: 'NotoSerifJP');
$config = (new Config())->withCssFeatureFlags(new CssFeatureFlags( layoutVerticalLr: true, layoutVerticalComposer: true,));
$factory = new DocumentFactory($fontRegistry, new ImageRegistry(maxCacheBytes: 0));$doc = $factory->create($config);$doc->setLanguage('ja');$doc->addPage();$doc->writeHtml( '<div style="writing-mode: vertical-rl; font-family: NotoSerifJP;">' . '縦書きの本文。' . '</div>',);$doc->save($out);Edge cases & gotchas
Section titled “Edge cases & gotchas”- Both flags are required. The composer needs
layoutVerticalLrandlayoutVerticalComposer. With either off, the run renders horizontally. - Real vertical metrics are required. A font without
vhea/vmtxcannot drive the composer; the run defers to horizontal layout. - Deferral is observable. A run that cannot compose emits
HTML_WRITING_MODE_LR_DEFERRED/HTML_WRITING_MODE_RL_DEFERREDwith areason. It never silently renders sideways. - No conformance claim from this path. Vertical composition is a layout capability; it is not a PDF/UA-2 or PDF/A-4 conformance statement for the produced file. A checker decides conformance.
Performance
Section titled “Performance”Composition adds a per-glyph vertical-advance lookup over the run, linear in
glyph count. The budget (wall_ms: 2000, peak_mb: 128) follows the CJK
profile, because vertical-metrics fonts are large and the dominant cost is font
handling, not the composition pass.
Security notes
Section titled “Security notes”The composer reads vertical metrics from already-registered, already-validated fonts. It does not open a new input channel. Font files remain untrusted binary input handled by the typography layer’s existing validation. Composed text is rendered, not interpreted.
Conformance
Section titled “Conformance”| Statement | Spec | Clause |
|---|---|---|
| Vertical writing uses CIDFont vertical glyph metrics for placement. | ISO 32000-2 | §9.7.5 |
writing-mode: vertical-lr / vertical-rl set the block-flow direction. | W3C CSS Writing Modes Level 3 | §3 |
| Per-glyph upright orientation follows the Unicode vertical-orientation property. | Unicode UAX #50 | Vertical Orientation |
This is a preview implementation of a single-column upright vertical subset with the documented fail-closed boundaries above. NextPDF does not assert that output from this path conforms to any profile; a checker makes that determination. No standards text is reproduced.