PDFlibPas fa scorrere un passaggio trattenuto attraverso da una a 64 colonne di uguale larghezza con DrawTextFlowColumns, e interrompe le sue righe con sillabazione limitata e sensibile alla lingua una volta chiamati SetTextFlowLanguage e SetTextFlowHyphenation. Sono supportate nove lingue, e la lingua può essere ereditata dal valore /Lang del Catalog del documento invece di essere impostata per ogni flow
Entrambe le funzionalità esistono per lo stesso motivo: una colonna stretta è il punto in cui l'interruzione di riga ingenua smette di somigliare a impaginazione e inizia a somigliare a un bug report
Perché il testo giustificato si sfalda in colonne strette?
Perché la giustificazione distribuisce lo spazio in eccesso negli interstizi tra le parole di una riga, e la quantità in eccesso dipende da cosa entra. Su una misura larga l'eccesso è piccolo e l'occhio non se ne accorge mai. Dimezza la larghezza e una singola parola lunga che non entra viene spinta alla riga successiva, lasciando che le sue predecessore assorbano tutto quello spazio. Tre righe del genere in sequenza producono i canali bianchi verticali che i tipografi chiamano fiumi, e che i lettori percepiscono come testo difficile da seguire senza sapere perché
La sillabazione risolve la causa invece del sintomo permettendo un'interruzione dentro la parola. I composti tedeschi e olandesi rendono questo non negoziabile: un sostantivo di 24 caratteri in una colonna di 60 millimetri non ha alcun esito buono senza un punto di interruzione. L'inglese tollera meglio l'assenza, ed è per questo che i prodotti pensati prima di tutto per l'inglese spesso distribuiscono codice di layout che crolla la prima volta che un cliente tedesco lo esegue
Quali lingue, e da dove arriva la lingua?
La sillabazione copre inglese, tedesco, olandese, francese, spagnolo, italiano, portoghese, russo e turco. Impostala esplicitamente per ogni flow con SetTextFlowLanguage, oppure lasciala ereditare dalla voce /Lang del Catalog del documento, che è il valore che un documento già taggato e accessibile porta con sé
Vale la pena usare questa eredità invece di sovrascriverla. Un documento che dichiara la propria lingua nel Catalog dice a screen reader, indicizzatori di ricerca e sillabazione lo stesso fatto da un unico posto, e un unico posto è dove un fatto dovrebbe vivere. Se stai già producendo output taggato come descritto in tagging automatico per PDF accessibili, la voce lingua è già impostata e il flow può semplicemente seguirla
var
Lib: TPDFlib;
Flow, Drawn: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.AddTrueTypeFont('Georgia', 1);
Lib.SetTextSize(10.5);
Flow := Lib.NewTextFlow(ArticleBody);
try
Lib.SetTextFlowLanguage(Flow, 'de');
// Attiva, almeno 3 caratteri prima dell'interruzione, 3 dopo
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
Lib.SetTextFlowMinLines(Flow, 2); // non lasciare mai isolata una singola riga
repeat
// Tre colonne su una regione di 480 pt, gutter di 18 pt, bilanciate
Drawn := Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);
if (Drawn = 0) or (Lib.TextFlowFinished(Flow) = 1) then
Break;
Lib.NewPage;
until False;
finally
Lib.ReleaseTextFlow(Flow);
end;
Lib.SaveToFile('newsletter.pdf');
finally
Lib.Free;
end;
end;
MinPrefix e MinSuffix sono tipografia, non validazione
I due interi dopo il flag di attivazione impostano il numero minimo di caratteri che devono restare prima e dopo un'interruzione. Tre e tre è un default prudente che la maggior parte degli stili editoriali accetta. Due e due produce più opportunità di interruzione e risultati sensibilmente più brutti, perché un frammento di due lettere penzolante alla fine di una riga si legge come un refuso
Alza i minimi quando il corpo del testo è grande, dove ogni frammento è visivamente prominente, e abbassali solo quando la colonna è genuinamente stretta e hai deciso che una misura stretta conta più di una pulita. Questa è una decisione di stile editoriale più che tecnica, ed è esattamente per questo che è un parametro invece di una costante
Cosa significa davvero «bilanciate» qui?
Il parametro Balance cambia comportamento solo alla fine di un passaggio. Con il bilanciamento attivo, le colonne vengono accorciate a un numero di righe esattamente uguale quando tutto ciò che resta entra dentro la regione, il che è ciò che impedisce a una pagina finale di mostrare due colonne piene e una terza con una sola riga solitaria. Quando il passaggio non entra, ogni colonna mantiene la sua altezza piena così la pagina porta quanto più testo possibile e il resto continua nella pagina successiva
Questa asimmetria è il default corretto per i documenti continui. Bilanciare nel mezzo di un articolo che scorre sprecherebbe spazio verticale su ogni pagina per un effetto cosmetico che nessuno vede, dato che le colonne sono comunque piene. Bilanciare alla fine è dove l'occhio se ne accorge davvero, ed è esattamente lì che si applica
L'interruzione di riga misura parole intere
L'algoritmo di interruzione misura parole complete invece di accumulare larghezze di carattere, e riserva una ricerca limitata per i token sovradimensionati che non entrano affatto in una riga, come un URL o un numero di protocollo. Questo mantiene veloce il caso comune e limitato il caso patologico, invece del contrario
I soft hyphen discrezionali e i trattini automatici vengono renderizzati solo quando l'interruzione che segnano è l'interruzione che viene scelta. Sembra ovvio ed è un difetto classico: un'implementazione ingenua scrive il carattere trattino mentre misura, e se l'interruzione si sposta, il trattino resta indietro nel mezzo di una riga. Nulla somiglia di più a un text engine rotto di un trattino vagante dentro una parola
var
Lib: TPDFlib;
Flow, Needed: Integer;
begin
// Decidi il layout prima di disegnare qualunque cosa
Flow := Lib.NewTextFlow(ArticleBody);
try
Lib.SetTextFlowLanguage(Flow, 'fr');
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
// Righe di cui il resto del passaggio ha bisogno a una larghezza di colonna
Needed := Lib.MeasureTextFlow(Flow, 148);
if Needed > 3 * LinesPerColumn then
UseTwoPageSpread
else
UseSinglePage;
Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);
if Lib.TextFlowFinished(Flow) <> 1 then
CarryOver(Lib.GetTextFlowRemaining(Flow));
finally
Lib.ReleaseTextFlow(Flow);
end;
end;
Mantieni identiche le impostazioni del font tra le caselle
Una regola governa ogni layout basato su flow e vale la pena dirla senza mezzi termini: DrawTextFlow, DrawTextFlowColumns e MeasureTextFlow interrompono tutti le righe usando il font selezionato nel momento in cui vengono chiamati. Cambia font o dimensione tra due caselle dello stesso flow, o inizia una nuova pagina senza riselezionarne uno, e la seconda casella si interrompe in modo diverso da ciò che la prima aveva misurato
Il sintomo è esasperante proprio perché sembra intermittente: testo che entra a pagina uno trabocca a pagina due, o un conteggio di righe misurato non concorda con ciò che è stato disegnato. Seleziona il font una volta prima del ciclo, riselezionalo dopo ogni NewPage, e il flow si comporta bene. Quando script misti compaiono nello stesso passaggio, la risoluzione descritta in fallback automatico dei font per CJK ed emoji si applica sia alla misurazione sia al disegno, così le larghezze restano coerenti anche lungo i run di fallback
Per i layout di report dove il flow è un elemento tra header, footer e blocchi guidati dai dati, i pattern di composizione in il motore di report dataset si combinano in modo pulito con i flow a colonne: misura prima, posiziona gli elementi fissi, poi dai al flow qualunque regione sia rimasta
PDFlibPas è una libreria PDF per Delphi, C++Builder e Lazarus, e l'intero ciclo di vita del TextFlow, creazione, disegno, misurazione, ispezione, rewind e rilascio, è esposto anche tramite le interfacce DLL e ActiveX. La documentazione completa è sulla pagina della libreria PDF Delphi PDFlibPas