Рендеринг арабского HTML с направлением справа налево
Коротко о главном
Заголовок раздела «Коротко о главном»Этот рецепт показывает, как рендерить HTML с направлением справа налево (RTL) в PDF с помощью writeHtml(). Задайте CSS-свойство direction: rtl и зарегистрируйте шрифт с поддержкой арабского. Движок переупорядочивает текст в визуальный порядок по алгоритму двунаправленного письма Unicode (UAX #9) и формирует арабские буквы в контекстные формы. В рецепте рендерится небольшой счёт на арабском. RTL используется для арабского, иврита, персидского и урду. Иврит переупорядочивается без формирования, и для этого письма это корректно.
Установка
Заголовок раздела «Установка»composer require nextpdf/coreТакже нужен шрифт TrueType или OpenType с поддержкой арабского. Его таблица символов должна покрывать блок Arabic Presentation Forms-B. Noto Naskh Arabic и Amiri — подходящие шрифты с открытыми лицензиями.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Для рендеринга RTL нужны два условия: CSS-свойство direction: rtl и зарегистрированный арабский шрифт.
direction: rtl указывает механизму компоновки размещать текст справа налево. Затем движок использует алгоритм двунаправленного письма Unicode (UAX #9), чтобы определить визуальный порядок. Смешанное содержимое упорядочивается корректно: латинские слова, арабские слова и цифры сохраняют своё направление. Цифры в числе, следующем за арабским текстом, остаются слева направо.
Арабское письмо — связное, поэтому каждая буква использует разные глифы в зависимости от соседних букв. Движок выбирает для каждой буквы начальную, срединную, конечную или изолированную форму и применяет лигатуру Lam-Alef. Для такого контекстного формирования нужен шрифт, чья таблица символов покрывает блок Arabic Presentation Forms-B. Шрифт, содержащий только латиницу, включая шрифты из стандартных 14, не может отрисовать арабский текст.
В таблице каждая ячейка переупорядочивается и формируется самостоятельно, а затем выравнивается по начальному краю: под direction: rtl это правый край. Логические значения text-align start и end интерпретируются относительно направления, поэтому start сопоставляется с правым краем для содержимого RTL.
Задавайте направление через CSS-свойство direction. HTML-атрибут dir не преобразуется в это свойство. См. RTL — текущие ограничения для текущих границ реализации.
Поверхность API
Заголовок раздела «Поверхность API»| Символ | Расположение | Роль |
|---|---|---|
Document::writeHtml(string $html): static | NextPDF\Core\Concerns\HasTextOutput | Рендерит фрагмент HTML в текущей позиции курсора. |
FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo | NextPDF\Typography\FontRegistry | Регистрирует арабский шрифт под псевдонимом. |
DocumentFactory::create(): Document | NextPDF\Core\DocumentFactory | Создаёт документ, использующий заполненный вами реестр. |
В примере используются эти CSS-свойства: direction, font-family, text-align. В CSS font-family обращайтесь к зарегистрированному шрифту по его псевдониму в реестре.
Пример кода — Быстрый старт
Заголовок раздела «Пример кода — Быстрый старт»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
$fontRegistry = new FontRegistry();$fontRegistry->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'ArabicFont');
$documentFactory = new DocumentFactory($fontRegistry, new ImageRegistry(maxCacheBytes: 0));$doc = $documentFactory->create();$doc->addPage();
$doc->writeHtml( '<div style="direction: rtl; font-family: \'ArabicFont\';">' . '<h1>فاتورة</h1>' . '<p>المبلغ الإجمالي 380.00</p>' . '</div>');
$doc->save(__DIR__ . '/rtl-arabic.pdf');Заголовок рендерится справа налево, а цифры 380.00 остаются слева направо внутри арабского предложения.
Пример кода — Производственный
Заголовок раздела «Пример кода — Производственный»Этот самодостаточный пример рендерит таблицу счёта на арабском. В каждой ячейке заданы direction: rtl и зарегистрированный арабский шрифт, поэтому движок переупорядочивает и формирует текст в каждой ячейке, а затем выравнивает ячейки по правому краю.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
// Supply an Arabic-capable face whose cmap covers Arabic Presentation Forms-B.// Embed only fonts you are licensed to embed.$fontPath = __DIR__ . '/NotoNaskhArabic-Regular.ttf';if (!is_file($fontPath)) { fwrite(STDERR, "Arabic font not found at {$fontPath}\n"); exit(1);}
$fontRegistry = new FontRegistry();$fontRegistry->register($fontPath, alias: 'ArabicFont');
$documentFactory = new DocumentFactory($fontRegistry, new ImageRegistry(maxCacheBytes: 0));$doc = $documentFactory->create();$doc->setTitle('Arabic invoice');$doc->addPage();
$html = <<<'HTML'<div style="direction: rtl; font-family: 'ArabicFont'; font-size: 12pt;"> <h1>فاتورة</h1> <table style="width: 100%; border-collapse: collapse;"> <tr> <th style="border: 1px solid #333; padding: 6px;">الوصف</th> <th style="border: 1px solid #333; padding: 6px;">المبلغ</th> </tr> <tr> <td style="border: 1px solid #333; padding: 6px;">خدمات استشارية</td> <td style="border: 1px solid #333; padding: 6px;">380.00</td> </tr> <tr> <td style="border: 1px solid #333; padding: 6px;">الإجمالي</td> <td style="border: 1px solid #333; padding: 6px;">380.00</td> </tr> </table></div>HTML;
$doc->writeHtml($html);
$out = getenv('NEXTPDF_OUT');$doc->save($out !== false ? $out : __DIR__ . '/render-rtl-arabic-html.pdf');
echo "Wrote the Arabic invoice PDF\n";Особые случаи и подводные камни
Заголовок раздела «Особые случаи и подводные камни»- Регистрируйте шрифт до создания документа.
Document::createStandalone()создаёт собственный реестр и не видит шрифт, зарегистрированный где-то ещё. Создавайте документ черезDocumentFactory, чтобы компонент записи использовал ваш реестр, как в обоих примерах. - Сопоставляйте CSS
font-familyс псевдонимом в реестре. Имя, которое вы передаёте вregister(..., alias: 'ArabicFont'), — это имя, на которое вы ссылаетесь в CSS. - Используйте CSS
direction, а не HTML-атрибутdir. Только CSS-свойство переключает направление компоновки. - Число после арабского остаётся слева направо. Так работает UAX #9: европейское число после арабской буквы разрешается в арабское число и сохраняет порядок своих цифр, поэтому
380.00не переворачивается.
RTL — текущие ограничения
Заголовок раздела «RTL — текущие ограничения»Текущая реализация переупорядочивает и формирует текст RTL и выравнивает ячейки таблиц. Эти ограничения сохраняются. Для каждого из них потребуется будущий построчный контекст встроенного форматирования:
- Выравнивание блочного и встроенного текста вне таблиц. Текст блочного и встроенного уровня вне ячеек таблиц переупорядочивается и формируется, но рендерится от начального (левого) края. Выравнивание нетабличного текста по правому краю или по центру, а также распределение
text-align: justify, пока не применяются. В ячейках таблиц выравнивание работает. - HTML-атрибут
dirне преобразуется вdirection. Задавайте направление через CSS-свойствоdirection. - Двунаправленное разрешение выполняется для каждого текстового фрагмента. Нейтральные символы между двумя встроенными элементами, такими как
<span>рядом с<b>, на одной строке не разрешаются. - Узкие арабские колонки измеряются по логическому тексту. Переносы строк рассчитываются по логическому тексту до формирования, поэтому очень узкая арабская колонка может перенести строку чуть раньше или позже.
- Сформированный арабский требует покрытия Presentation Forms-B. Шрифт должен покрывать блок Arabic Presentation Forms-B. Шрифты, опирающиеся только на подстановку OpenType GSUB, и формирование через HarfBuzz остаются будущей работой. Неарабский шрифт не может отрисовать арабский.
Производительность
Заголовок раздела «Производительность»Рендеринг масштабируется линейно с числом глифов. Двунаправленное переупорядочивание и контекстное формирование выполняются построчно и добавляют небольшой постоянный множитель по сравнению с текстом слева направо. Бюджет этого рецепта — wall_ms: 1500, peak_mb: 64.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»Проверяйте длину строк, полученных от пользователя, чтобы ограничить размер вывода. Движок рендерит текст, не интерпретирует его и не выполняет никаких сценариев. Если вы загружаете шрифт из удалённого источника @font-face, загрузкой управляет политика безопасных внешних ресурсов; предпочитайте локальный файл шрифта для предсказуемого вывода.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Спецификация | Пункт | reference_id |
|---|---|---|---|
| Визуальный порядок получается переворотом прогонов символов от наивысшего уровня вниз до наименьшего нечётного уровня. | Unicode UAX #9 | §3.3.6 Rule L2 (uax9#3.3.6.p13) | 814977a77019d728dc562a612098a82dc260f6844f5998eca5fe7a3baf3394af |
| Европейское число после арабской буквы разрешается в арабское число, поэтому его цифры сохраняют порядок слева направо. | Unicode UAX #9 | §3.3.4 Rule W2 (uax9#3.3.4.p9) | 5747405357772797d62b3f4ba79328557fa0c4273a1dd5ffa8d996f24c78e120 |
Контекстное формирование арабского (начальная, срединная, конечная и изолированная формы плюс лигатура Lam-Alef) — проверенная возможность движка, покрытая набором тестов, а не отдельное заявление о соответствии. Оно требует шрифта, чья таблица символов покрывает блок Arabic Presentation Forms-B.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»Неприменимо.
См. также
Заголовок раздела «См. также»- HTML: подсистема рендеринга HTML+CSS в PDF — движок
writeHtml()и его поддержка RTL. - Типографика: реестр шрифтов, субсеттинг, CMap, кодировка, BiDi — движок двунаправленного письма и обработка UAX #9.
- Матрица поддержки шрифтов и письменностей — какие письменности отрисовывает каждый класс шрифтов.
- Рендеринг HTML в страницу PDF — отправная точка для документов с письмом слева направо.