跳到內容

繪製由右至左的阿拉伯文 HTML

使用 writeHtml() 將由右至左(RTL)的 HTML 繪製成 PDF。設定 CSS direction: rtl 屬性,並註冊支援阿拉伯文的字型。引擎會依 Unicode 雙向演算法(UAX #9)將文字重排為視覺順序,並把阿拉伯文字母塑形成對應的上下文形式。本範例會繪製一張小型阿拉伯文發票。RTL 適用於阿拉伯文、希伯來文、波斯文與烏都文。希伯來文會重排,但不會塑形;對該文字系統而言,這是正確行為。

Terminal window
composer require nextpdf/core

你還需要一個支援阿拉伯文的 TrueType 或 OpenType 字型。該字型的字元對應表(character map)必須涵蓋 Arabic Presentation Forms-B 區塊。Noto Naskh Arabic 與 Amiri 都是適合的開放授權字體。

RTL 繪製需要兩項輸入:CSS direction: rtl 屬性,以及已註冊的阿拉伯文字型。

direction: rtl 會告訴版面配置由右至左排列文字。接著,引擎會使用 Unicode 雙向演算法(UAX #9)決定視覺順序。混合內容會正確排序:拉丁文字、阿拉伯文字與數字各自保留自己的方向。緊接在阿拉伯文之後的數字,會保持由左至右。

阿拉伯文是一種草書(cursive)文字系統,因此每個字母會依相鄰字母採用不同字形。引擎會為每個字母選擇起首形(initial)、中間形(medial)、結尾形(final)或孤立形(isolated),並套用 Lam-Alef 連字。這種上下文塑形需要字元對應表涵蓋 Arabic Presentation Forms-B 區塊的字型。僅含拉丁文字的字型(包含 standard-14 字體)無法繪製阿拉伯文。

在表格中,每個儲存格都會各自重排與塑形,並對齊起始邊;在 direction: rtl 之下,起始邊就是右邊。邏輯性的 text-alignstartend 會依方向解析,因此對 RTL 內容而言,start 會對應到右邊。

請以 CSS direction 屬性設定方向。HTML dir 屬性不會對應到它。關於目前的實作邊界,請參見 RTL — 目前的限制

符號位置角色
Document::writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutput在目前游標位置繪製 HTML 片段。
FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry以別名註冊阿拉伯文字體。
DocumentFactory::create(): DocumentNextPDF\Core\DocumentFactory建立會讀取你已填入登錄表(registry)的文件。

範例會使用這些 CSS 屬性:directionfont-familytext-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 在這個阿拉伯文句子中仍會保持由左至右。

這個自足(self-contained)的範例會繪製一張阿拉伯文發票表格。每個儲存格都帶有 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";
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 文字,也會對齊表格儲存格。以下邊界仍然存在。每一項都需要未來提供逐行的行內格式化行框(inline-formatting line box):

  • 表格之外的區塊與行內對齊。 表格儲存格之外的區塊層級與行內文字會被重排與塑形,但仍會從起始邊(左邊)繪製。非表格文字的靠右、置中對齊,以及 text-align: justify 的分散對齊,目前都尚未套用。表格儲存格則會對齊。
  • HTML dir 屬性不會對應到 direction 請以 CSS direction 屬性設定方向。
  • 雙向解析是以每個文字段落(text run)為單位。 中性字元不會跨越同一行上的兩個行內元素進行解析,例如 <span> 緊鄰一個 <b>
  • 狹窄的阿拉伯文欄位是依邏輯文字來量測。 換行是依邏輯且尚未塑形的文字來量測,因此非常狹窄的阿拉伯文欄位可能會稍早或稍晚斷行。
  • 塑形後的阿拉伯文需要 Presentation Forms-B 的涵蓋範圍。 字體必須涵蓋 Arabic Presentation Forms-B 區塊。僅倚賴 OpenType GSUB 替換的字型,以及 HarfBuzz 塑形路徑,都屬於未來的工作。非阿拉伯文字型無法繪製阿拉伯文。

繪製會隨字形數量線性擴展。雙向重排與上下文塑形會逐行執行;相較於由左至右的文字,只會增加一個小的常數因子。本範例的預算為 wall_ms: 1500, peak_mb: 64

請驗證使用者提供字串的長度,將輸出大小維持在可控範圍內。引擎會繪製文字,但不會解讀文字,也不會執行任何指令稿(script)。如果你透過遠端 @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 區塊的字型。

不適用。