Перейти к содержимому
getnextpdf.com

стабильность: Экспериментальная

Поддержка компоновки сложных письменностей

Подключаемое превью. Компоновка сложных письменностей по умолчанию выключена. Когда она выключена, движок рендерит через существующий путь кодовая точка → cmap — побайтово идентично сборке без этой функции. Включайте её только тогда, когда у вас есть libharfbuzz и шрифт, способный к компоновке, и проверяйте результат.

Рендерер HTML добавляет подключаемый компоновщик сложных письменностей для тибетской и монгольской. Когда компоновщик включён, обнаруженный прогон в области действия — тибетский или монгольский — компонуется через libharfbuzz и выдаётся как коды глифов Identity-H. Компоновщик покрывает горизонтальный тибетский в начертаниях TrueType и CFF/OTTO, включая перенос строк, и вертикальный монгольский, набираемый сверху вниз (TTB).

Окно терминала
composer require nextpdf/core:^3

Компоновщик поставляется в пакете core. Подключаемая опция CssFeatureFlags::$complexTextShaping помечена @since 6.1.0. libharfbuzz — требование времени выполнения, когда флаг включён — компоновщик вызывает libharfbuzz через расширение PHP FFI. Когда флаг выключен, у библиотеки нет зависимости от libharfbuzz.

Сложные письменности переупорядочивают, подставляют и перепозиционируют глифы по контексту. Наивное отображение кодовая точка → глиф рендерит их явно неверно. Компоновщик передаёт прогон в области действия в libharfbuzz, который применяет таблицы компоновки OpenType шрифта, а движок выдаёт получившуюся последовательность глифов как составной шрифт Type 0 с кодированием Identity-H (ISO 32000-2 §9.7.4 — показываемая строка — это двухбайтовые CID).

Область действия намеренна. Компоновщик распознаёт тибетские и монгольские прогоны и компонует их; он не претендует на общее покрытие сложных письменностей. Горизонтальный тибетский компонуется в начертаниях TrueType и CFF/OTTO с переносом строк. Монгольский компонуется вертикально, сверху вниз.

Граница с отказом закрытием — жёсткая и типизированная

Заголовок раздела «Граница с отказом закрытием — жёсткая и типизированная»

Компоновщик никогда не выдаёт нескомпонованные, визуально сломанные глифы как запасной вариант. Прогон, который нельзя скомпоновать точно, вместо этого вызывает типизированное исключение:

  • ComplexScriptShapingException — прогон нельзя скомпоновать точно: в шрифте нет нужных глифов (получился бы .notdef), начертание CFF просят компоновать по вертикальному пути, прогон содержит ссылку или монгольской колонке нужен перенос (случай вне области действия).
  • HarfBuzzUnavailableException — флаг включён, но libharfbuzz недостижима через FFI во время выполнения.

При выключенном флаге прогон в области действия рендерится через существующий путь кодовая точка → cmap. Это задокументированное ограничение, а не заявление о компоновке: путь с выключенным флагом не применяет компоновку OpenType, поэтому контекстные формы не гарантируются. Не описывайте вывод этого пути как «скомпонованный».

Граница честности — объективный паритет, а не эстетическое одобрение

Заголовок раздела «Граница честности — объективный паритет, а не эстетическое одобрение»

Точность компоновки проверяется объективно относительно HarfBuzz: идентификаторы выдаваемых глифов, отображение кластеров и позиции глифов совпадают с эталонным выводом HarfBuzz (паритет по глифу, кластеру и позиции). Эстетическая проверка носителем языка — оценка того, читается ли результат естественно для свободно владеющего читателя, — это отслеживаемое продолжение после выпуска. NextPDF не делает никакого заявления о языковом качестве в этом API или в этих документах. Объективный паритет утверждается; эстетическое качество — нет.

СимволРасположениеРоль
CssFeatureFlags::$complexTextShapingsrc/Html/CssFeatureFlags.phpПодключаемый флаг для тибетского/монгольского компоновщика (по умолчанию false).
Config::withCssFeatureFlags(CssFeatureFlags $flags): selfsrc/Core/Config.phpПрисоединяет набор флагов к конфигурации документа.
ComplexScriptShapingExceptionsrc/Font/Shaper/ComplexScriptShapingException.phpВыбрасывается, когда прогон в области действия нельзя скомпоновать точно.
HarfBuzzUnavailableExceptionsrc/Font/Shaper/HarfBuzzUnavailableException.phpВыбрасывается, когда флаг включён, но libharfbuzz недоступна.
<?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(complexTextShaping: true),
);
$doc = Document::createStandalone($config);
$doc->addPage();
$doc->writeHtml(
'<div style="font-family: NotoSerifTibetan;">བོད་སྐད་</div>',
);
$doc->save(__DIR__ . '/tibetan.pdf');

Зарегистрируйте шрифт, способный к компоновке, подключите компоновщик и явно обработайте два типизированных режима отказа. Точный рендеринг или ясное исключение — но никогда молча сломанный прогон глифов.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;
use NextPDF\Core\DocumentFactory;
use NextPDF\Exception\ComplexScriptShapingException;
use NextPDF\Exception\HarfBuzzUnavailableException;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Html\Css\CssFeatureFlags;
use NextPDF\Typography\FontRegistry;
$fontRegistry = new FontRegistry();
$fontRegistry->register('/path/to/NotoSerifTibetan-Regular.ttf', alias: 'NotoSerifTibetan');
$config = (new Config())->withCssFeatureFlags(
new CssFeatureFlags(complexTextShaping: true),
);
$factory = new DocumentFactory($fontRegistry, new ImageRegistry(maxCacheBytes: 0));
$doc = $factory->create($config);
$doc->setLanguage('bo');
$doc->addPage();
try {
$doc->writeHtml('<div style="font-family: NotoSerifTibetan;">བོད་སྐད་</div>');
} catch (HarfBuzzUnavailableException $e) {
// The flag is on but libharfbuzz is not reachable. Install it, or turn the
// flag off to fall back to the unshaped cmap path.
throw $e;
} catch (ComplexScriptShapingException $e) {
// The run cannot be shaped faithfully (missing glyphs, link in run,
// out-of-scope case). Fix the font or the content; do not ship broken glyphs.
throw $e;
}
$doc->save($out);
  • libharfbuzz требуется, когда включено. При включённом флаге и отсутствующей libharfbuzz движок выбрасывает HarfBuzzUnavailableException. Он не деградирует молча.
  • Выключено — это не «скомпоновано». При выключенном флаге прогон в области действия рендерится через путь cmap без компоновки OpenType. Это задокументированное ограничение; не называйте это скомпонованным выводом.
  • Область действия — тибетская и монгольская. Другие сложные письменности вне области этого слоя.
  • Ссылка в прогоне отказывает закрытием. Прогон, содержащий аннотацию-ссылку, вызывает ComplexScriptShapingException, потому что прямоугольник ссылки не может следовать за переупорядочением при компоновке.
  • Нет заявления о языковом качестве. Паритет с HarfBuzz утверждается; эстетическое качество для носителя языка — отслеживаемое продолжение и не заявляется.

Компоновка добавляет один вызов libharfbuzz на прогон в области действия плюс проход выдачи глифов, линейный по числу глифов. Бюджет (wall_ms: 2000, peak_mb: 128) следует профилю CJK/сложных письменностей, потому что шрифты для компоновки велики и обработка шрифта доминирует в стоимости.

Включение компоновщика вводит вызов FFI в libharfbuzz, нативную библиотеку. Файлы шрифтов остаются недоверенным бинарным вводом, обрабатываемым существующей валидацией слоя типографики, прежде чем они достигают компоновщика. Компоновщик потребляет уже зарегистрированные, уже проверенные начертания. Считайте происхождение шрифтов, предоставленных конечным пользователем, недоверенным и предоставляйте libharfbuzz из доверенного источника.

УтверждениеСпецификацияРаздел
Скомпонованные прогоны выдаются как двухбайтовые CID Identity-H в составном шрифте Type 0.ISO 32000-2§9.7.4
Компоновщик применяет подстановку и позиционирование глифов OpenType шрифта.OpenType SpecificationGSUB / GPOS
Формирование кластеров следует свойствам письменности для тибетской и монгольской.Unicode Standard AnnexTibetan and Mongolian

Это превью-реализация, ограниченная тибетской и монгольской, проверенная на объективный паритет с HarfBuzz. Она не делает заявления о языковом качестве и не утверждает сквозное соответствие PDF для созданного файла. Текст стандартов не воспроизводится.