Pular para o conteúdo

Renderizar HTML árabe da direita para a esquerda

Renderize HTML da direita para a esquerda (RTL) em PDF com writeHtml(). Defina a propriedade CSS direction: rtl e registre uma fonte com suporte a árabe. O motor reordena o texto para a ordem visual com o Algoritmo Bidirecional Unicode (UAX #9) e molda as letras árabes em formas contextuais. Esta receita renderiza uma pequena fatura em árabe. RTL é usado em árabe, hebraico, persa e urdu. O hebraico é reordenado, mas não moldado, o que é correto para essa escrita.

Terminal window
composer require nextpdf/core

Você também precisa de uma fonte TrueType ou OpenType com suporte a árabe. O mapa de caracteres dela deve cobrir o bloco Arabic Presentation Forms-B. Noto Naskh Arabic e Amiri são fontes adequadas com licença aberta.

Você precisa de dois elementos para a renderização RTL: a propriedade CSS direction: rtl e uma fonte árabe registrada.

direction: rtl indica ao layout que posicione o texto da direita para a esquerda. O motor então usa o Algoritmo Bidirecional Unicode (UAX #9) para resolver a ordem visual. Conteúdo misto é ordenado corretamente: palavras em latim, palavras em árabe e dígitos preservam suas próprias direções. Um número que segue texto em árabe mantém os dígitos da esquerda para a direita.

O árabe é uma escrita cursiva, portanto cada letra usa um glifo diferente conforme seus vizinhos. O motor seleciona a forma inicial, medial, final ou isolada de cada letra e aplica a ligadura Lam-Alef. Essa moldagem contextual precisa de uma fonte cujo mapa de caracteres cubra o bloco Arabic Presentation Forms-B. Uma fonte apenas latina, incluindo as fontes standard-14, não consegue renderizar árabe.

Em uma tabela, cada célula é reordenada e moldada de forma independente, e as células se alinham à borda inicial: a borda direita sob direction: rtl. Os valores lógicos de text-align start e end são resolvidos em relação à direção; portanto, start mapeia para a borda direita em conteúdo RTL.

Defina a direção com a propriedade CSS direction. O atributo HTML dir não é mapeado para ela. Consulte RTL — limitações atuais para conhecer os limites atuais da implementação.

SímboloLocalizaçãoFunção
Document::writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutputRenderiza o fragmento HTML na posição atual do cursor.
FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistryRegistra a fonte árabe sob um alias.
DocumentFactory::create(): DocumentNextPDF\Core\DocumentFactoryCria um documento que lê seu registro preenchido.

O exemplo usa estas propriedades CSS: direction, font-family, text-align. Referencie a fonte registrada no CSS font-family pelo alias de registro.

<?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');

O título é renderizado da direita para a esquerda, e os dígitos 380.00 permanecem da esquerda para a direita dentro da frase em árabe.

Este exemplo autossuficiente renderiza uma tabela de fatura em árabe. Cada célula define direction: rtl e a fonte árabe registrada; com isso, o motor reordena e molda cada linha e, em seguida, alinha as células à borda direita.

<?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";">
  • Registre a fonte antes de construir o documento. Document::createStandalone() constrói seu próprio registro e não consegue acessar uma fonte que você registrou em outro lugar. Construa por meio de DocumentFactory para que o escritor leia seu registro, como fazem os dois exemplos.
  • Faça o CSS font-family corresponder ao alias de registro. O nome que você passa para register(..., alias: 'ArabicFont') é o nome que você referencia no CSS.
  • Use o CSS direction, não o atributo HTML dir. Apenas a propriedade CSS alterna o layout.
  • Um número após texto em árabe permanece da esquerda para a direita. Isso segue o UAX #9: um número europeu após uma letra árabe resolve-se como número árabe e mantém a ordem dos dígitos; portanto, 380.00 não é invertido.

A implementação atual reordena e molda o texto RTL e alinha as células de tabela. As limitações abaixo permanecem. Cada uma depende de uma futura caixa de linha de formatação inline por linha:

  • Alinhamento de bloco e inline fora de tabelas. Texto em nível de bloco e inline fora de células de tabela é reordenado e moldado, mas é renderizado a partir da borda inicial (esquerda). O alinhamento à direita ou ao centro para texto fora de tabela, e a distribuição text-align: justify, ainda não são aplicados. As células de tabela são alinhadas.
  • O atributo HTML dir não mapeia para direction. Defina a direção com a propriedade CSS direction.
  • A resolução bidirecional é por sequência de texto (run). Caracteres neutros não são resolvidos entre dois elementos inline, como um <span> ao lado de um <b>, na mesma linha.
  • Colunas árabes estreitas medem com base no texto lógico. As quebras de linha são medidas com base no texto lógico, anterior à moldagem, então uma coluna árabe muito estreita pode quebrar uma linha um pouco cedo ou tarde demais.
  • O árabe moldado precisa da cobertura de Presentation Forms-B. A fonte deve cobrir o bloco Arabic Presentation Forms-B. Fontes que dependem apenas da substituição OpenType GSUB, e o caminho de moldagem HarfBuzz, ficam para trabalho futuro. Uma fonte não árabe não consegue renderizar árabe.

A renderização escala linearmente com o número de glifos. A reordenação bidirecional e a moldagem contextual são executadas por linha e adicionam um pequeno fator constante em relação ao texto da esquerda para a direita. O orçamento desta receita é wall_ms: 1500, peak_mb: 64.

Valide o comprimento das strings fornecidas pelo usuário para limitar o tamanho da saída. O motor renderiza o texto; ele não o interpreta nem executa scripts. Se você carregar uma fonte por meio de uma origem remota @font-face, a política de recurso externo seguro controla a busca; prefira um arquivo de fonte local para uma saída previsível.

DeclaraçãoSpecCláusulareference_id
A ordem visual é produzida invertendo as sequências de caracteres do nível mais alto até o menor nível ímpar.Unicode UAX #9§3.3.6 Rule L2 (uax9#3.3.6.p13)814977a77019d728dc562a612098a82dc260f6844f5998eca5fe7a3baf3394af
Um número europeu após uma letra árabe resolve-se como número árabe, então seus dígitos mantêm a ordem da esquerda para a direita.Unicode UAX #9§3.3.4 Rule W2 (uax9#3.3.4.p9)5747405357772797d62b3f4ba79328557fa0c4273a1dd5ffa8d996f24c78e120

A moldagem contextual do árabe (formas inicial, medial, final e isolada, além da ligadura Lam-Alef) é uma capacidade verificada do motor, coberta pela suíte de testes, e não uma declaração de conformidade separada. Ela requer uma fonte cujo mapa de caracteres cubra o bloco Arabic Presentation Forms-B.

Não aplicável.