Articolo tecnico

Testo verticale CJK in PDF con Delphi e HotPDF

Imposta un romanzo giapponese sulla pagina e la prima cosa che noti è che il testo scorre lungo la colonna, non attraverso la riga, e che le colonne avanzano dal bordo destro del foglio verso sinistra. Un lettore cresciuto con questo sistema trova il testo orizzontale vagamente asettico. Il problema ingegneristico è che il PDF, come quasi ogni sistema di testo digitale, è stato costruito attorno a una linea di base orizzontale che cresce da sinistra a destra, e un flusso di contenuti non ha la nozione di "scrivi invece questo paragrafo verso il basso". Pertanto, quando un'applicazione Delphi deve produrre un certificato, una poesia, una segnaletica o un documento legale in formato tradizionale per un lettore taiwanese, giapponese o coreano, il layout deve essere assemblato a mano: un carattere sotto il successivo, una colonna a sinistra della precedente

HotPDF ti offre un'opzione che gestisce per te la contabilità carattere per carattere. Il font impostato porta con sé un flag IsVertical, e una volta attivato, una singola chiamata a TextOut impila un'intera stringa in una colonna verticale invece di farla scorrere lungo una linea di base. Il posizionamento delle colonne, l'ordine da destra a sinistra e una sostituzione di glifi discretamente importante sono ciò di cui si occupa il resto di questa pagina

Una singola pagina A4 con testo in cinese, giapponese e coreano in colonne verticali, generata da un'unica procedura Delphi
Una singola pagina A4 con testo in cinese, giapponese e coreano in colonne verticali, generata da un'unica procedura Delphi

L'opzione risiede in SetFont

Il layout verticale non è una proprietà della pagina o del documento. È una proprietà del font con cui si disegna e si attiva con il quinto argomento di SetFont:

// SetFont(FontName, FontStyle, Size, FontCharset, IsVertical)
// Il 5° argomento imposta il font corrente in modalità verticale.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, False); // orizzontale
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, True);  // verticale

Poiché il flag è legato al font, si passa dalla scrittura orizzontale a quella verticale semplicemente chiamando di nuovo SetFont con un ultimo argomento diverso. Una pagina può contenere un titolo orizzontale in alto e testo verticale in basso, e HotPDF mantiene le due modalità separate per oggetto font anziché per pagina. Questo è ciò che rende possibili layout misti senza alcuna gestione speciale della modalità da parte tua: ogni TextOut dopo un SetFont verticale si impila, ogni TextOut dopo uno orizzontale scorre lungo la linea di base, e l'ultimo SetFont ha la meglio

Il quarto argomento è il set di caratteri di Windows, lo stesso accettato da una chiamata orizzontale. Passare DEFAULT_CHARSET consente al sistema di risolvere i glifi per stringa, il che è importante qui perché un documento verticale spesso mescola alfabeti. Tutto il resto riguardo al font rimane valido: deve essere installato sulla macchina di compilazione, e quasi sempre si desidera FontEmbedding := True in modo che il file esegua il rendering degli stessi glifi CJK su un lettore che non ha mai avuto Arial Unicode MS

Una chiamata a TextOut è una colonna

Con un font verticale attivo, una chiamata a TextOut non estende più la sua stringa lateralmente dal punto specificato. Pianta il primo carattere in alto e fa scorrere il resto dritto verso il basso, avanzando dell'altezza della riga del font per ogni glifo. La X passata fissa la colonna; la Y passata fissa dove inizia la parte superiore della colonna. Per disporre un passaggio reale, si emette quindi un TextOut per colonna e si sposta la X verso sinistra tra le chiamate, perché le colonne CJK si leggono da destra a sinistra

var
  Pdf: THotPDF;
const
  ColTop = 760;   // y del primo glifo in ogni colonna (punti)
  ColGap = 28;    // distanza orizzontale tra le colonne
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'VerticalText.pdf';
    Pdf.FontEmbedding := True;       // incorpora il font CJK per rendering portabile
    Pdf.BeginDoc;
    Pdf.CurrentPage.Size := psA4;

    // Prima un'intestazione orizzontale, nella modalità di scrittura ordinaria.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 16, DEFAULT_CHARSET, False);
    Pdf.CurrentPage.TextOut(60, 800, 0, 'Tang poem, vertical layout');

    // Cambia il font in modalità verticale; ogni TextOut sottostante ora si impila.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 18, DEFAULT_CHARSET, True);

    // Le colonne avanzano da destra a sinistra, quindi X diminuisce a ogni chiamata.
    Pdf.CurrentPage.TextOut(520, ColTop, 0, '床前明月光');
    Pdf.CurrentPage.TextOut(520 - ColGap,     ColTop, 0, '疑是地上霜');
    Pdf.CurrentPage.TextOut(520 - ColGap * 2, ColTop, 0, '舉頭望明月');
    Pdf.CurrentPage.TextOut(520 - ColGap * 3, ColTop, 0, '低頭思故鄉');

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

La poesia si legge come dovrebbe: prima la colonna più a destra, dall'alto verso il basso, poi l'occhio salta a sinistra alla colonna successiva. Nulla nella stringa stessa codifica quell'ordine. Lo si codifica nelle coordinate X, diminuendole chiamata per chiamata. Sbagliando la direzione, i versi escono invertiti, che è l'errore più comune quando si converte un layout da orizzontale a verticale

Coordinate: giù per la pagina, da destra a sinistra

Due assi sono in gioco e tirano in direzioni opposte, quindi vale la pena essere precisi. HotPDF misura dall'angolo in basso a sinistra della pagina, con Y che aumenta verso l'alto, in punti. Una colonna verticale quindi inizia con una Y alta (vicino alla parte superiore del foglio) e i caratteri scendono da lì mentre HotPDF sottrae l'altezza di riga per ciascuno di essi. Si imposta quella Y iniziale una volta per colonna e il componente gestisce la discesa

L'asse orizzontale è quello gestito manualmente. Ogni colonna ha la sua coordinata X, e le colonne successive passano a valori X più piccoli perché l'ordine di lettura è da destra a sinistra. Un ritmo sensato è scegliere la X della colonna più a destra, quindi sottrarre uno spazio fisso per ogni chiamata successiva, come fa l'esempio con ColGap. La scelta dello spazio dipende da te; troppo stretto e le colonne adiacenti si toccano, troppo largo e il blocco sembra dispersivo. Per il corpo del testo da 12 a 18 punti, uno spazio leggermente più grande della dimensione del font si legge comodamente

Glifi che devono cambiare forma

Alcuni caratteri non sono semplicemente copie ruotate di se stessi in orizzontale; devono essere sostituiti quando il testo diventa verticale. Quello che HotPDF gestisce per te è il segno di suono prolungato giapponese (U+30FC), la barra di vocale lunga che appare in parole katakana come コーヒー. Disegnato orizzontalmente è un breve trattino sulla linea di base. Se si impilasse lo stesso glifo in una colonna, rimarrebbe piatto attraverso la colonna, il che è sbagliato: nel giapponese verticale il segno diventa un tratto verticale che collega i due caratteri tra cui si trova. HotPDF rileva U+30FC nel percorso verticale e ne esegue il rendering come barra verticale (U+007C) in modo che il segno di vocale lunga punti nella direzione giusta senza alcun lavoro da parte tua

Questa singola sostituzione copre il caso che interrompe la maggior parte delle implementazioni ingenue, ma vale la pena sapere dove il problema generale va più a fondo. La tipografia verticale completa ruota anche le lettere latine e la punteggiatura occidentale di novanta gradi, sposta i piccoli kana e riposiziona parentesi e virgole nelle loro forme verticali, e un'implementazione completa di ciò risiede nelle funzionalità OpenType verticali di un font piuttosto che in regole fisse. HotPDF può sfruttare queste funzionalità quando il font le contiene: le alternative verticali (le funzionalità GSUB vert e vrt2) e la crenatura verticale (le ricerche GPOS vkrn e vpal) sono facoltative, applicate lungo il percorso verticale solo quando il font attivo le definisce effettivamente. Per testo misto CJK in un unico carattere ad ampia copertura come Arial Unicode MS, la gestione integrata di U+30FC più un passo di altezza della riga uniforme è sufficiente per produrre colonne corrette e leggibili; le funzionalità OpenType diventano importanti quando si passa a un font progettato per una precisa composizione verticale e se ne desidera il posizionamento nativo dei kana e la spaziatura tra glifi

Mescolare script e orientamenti in una pagina

I documenti reali sono raramente puri. Una pagina giapponese verticale potrebbe contenere una didascalia in inglese in orizzontale, un numero di pagina in basso o un blocco di coreano impostato verticalmente accanto al giapponese. Poiché il flag verticale è un'opzione a livello di font, questi elementi vengono composti alternando le chiamate a SetFont piuttosto che gestendo uno stato per l'intera pagina. Si imposta un font orizzontale, si scrive l'intestazione corrente e il numero di pagina, si imposta un font verticale, si dispongono le colonne, si imposta di nuovo un font orizzontale per un piè di pagina. Ogni area assume la modalità del SetFont più recente, quindi l'unica disciplina richiesta è chiamarlo ogni volta che si cambia direzione

Un dettaglio da pianificare quando gli script si mescolano: gli ideogrammi cinesi, giapponesi e coreani sono quasi quadrati e si impilano a passo uniforme, ma le sequenze latine incorporate in una colonna verticale non hanno tale avanzamento uniforme. Se occorrono alcune parole latine all'interno di testo altrimenti verticale, decidi deliberatamente se devono essere ruotate per scorrere lungo la colonna o impostate in verticale come un breve riquadro orizzontale, e posiziona quel frammento con un proprio TextOut piuttosto che lasciarlo seguire il passo verticale destinato agli ideogrammi. Trattare la sequenza mista come un problema di posizionamento a sé stante mantiene intatto il ritmo della colonna

Dall'esempio alla produzione

I pezzi sono piccoli e si compongono in modo prevedibile. Attiva il flag verticale in SetFont, emetti un TextOut per colonna da una Y fissa in alto, e diminuisci la X tra le chiamate in modo che le colonne si leggano da destra a sinistra. Incorpora il font in modo che i glifi CJK sopravvivano al viaggio verso la macchina di un lettore, e lascia che il componente gestisca il segno di vocale lunga U+30FC e, laddove il font li supporta, le funzionalità verticali OpenType. Da lì, portare il layout in produzione è per lo più aritmetica: derivare le posizioni X delle colonne dalla larghezza misurata di un blocco, suddividere i lunghi passaggi in colonne che si adattino all'altezza della pagina, e riservare spazio per intestazioni orizzontali e numeri di pagina

Per l'ampia base di testo e font su cui questo si costruisce, incluse le convenzioni per TextOut orizzontale e la registrazione dei font Unicode, consulta l'esempio multilingue Hello World e l'esempio TextOut. L'opzione verticale in SetFont e le chiamate TextOut mostrate qui fanno parte del Componente HotPDF per Delphi e C++Builder