PDFlibPas curge un pasaj reținut prin una până la 64 de coloane cu lățime egală cu DrawTextFlowColumns, și își întrerupe liniile cu despărțire în silabe mărginită, adaptată limbii, odată ce apelezi SetTextFlowLanguage și SetTextFlowHyphenation. Sunt suportate nouă limbi, iar limba poate fi moștenită din valoarea Catalog /Lang a documentului, nu setată per flux
Ambele funcționalități există din același motiv: o coloană îngustă este locul unde întreruperea naivă de linii încetează să arate ca tipografie și începe să arate ca un bug report
De ce se destramă textul justificat în coloane înguste?
Pentru că justificarea distribuie spațiul rămas în golurile dintre cuvinte ale unei linii, iar cantitatea rămasă depinde de ce încape. La o măsură largă, ce rămâne este mic, iar ochiul nu observă niciodată. Înjumătățește lățimea și un singur cuvânt lung care nu încape trece pe linia următoare, lăsându-i pe predecesorii lui să absoarbă tot acel spațiu. Trei astfel de linii la rând produc canalele albe verticale pe care tipografii le numesc râuri, iar cititorii le experimentează ca text greu de urmărit fără să știe de ce
Despărțirea în silabe rezolvă cauza, nu simptomul, permițând o întrerupere în interiorul cuvântului. Cuvintele compuse din germană și olandeză fac asta non-negociabil: un substantiv de 24 de caractere într-o coloană de 60 de milimetri nu are niciun rezultat bun fără un punct de întrerupere. Engleza tolerează absența mai bine, motiv pentru care produsele care pun engleza pe primul loc adesea livrează cod de layout care se prăbușește prima dată când un client german îl rulează
Care limbi, și de unde vine limba?
Despărțirea în silabe acoperă engleza, germana, olandeza, franceza, spaniola, italiana, portugheza, rusa și turca. Setează-o explicit per flux cu SetTextFlowLanguage, sau las-o moștenită din intrarea Catalog /Lang a documentului, care este valoarea pe care un document tagged și accesibil o poartă deja
Acea moștenire merită folosită, nu suprascrisă. Un document care își declară limba în Catalog spune aceeași afirmație cititoarelor de ecran, indexatoarelor de căutare și despărțirii în silabe dintr-un singur loc, iar un singur loc este unde ar trebui să trăiască un fapt. Dacă produci deja output tagged, așa cum este descris în taggarea automată pentru PDF-uri accesibile, intrarea de limbă este setată, iar fluxul o poate urma pur și simplu
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');
// Activează, cel puțin 3 caractere înainte de întrerupere, 3 după
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
Lib.SetTextFlowMinLines(Flow, 2); // nu lăsa niciodată o singură linie izolată
repeat
// Trei coloane pe o regiune de 480 pt, gutere de 18 pt, echilibrate
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 și MinSuffix sunt tipografie, nu validare
Cele două numere întregi de după flag-ul de activare setează numărul minim de caractere care trebuie să rămână înainte și după o întrerupere. Trei și trei este o valoare implicită conservatoare pe care majoritatea stilurilor de casă o acceptă. Doi și doi produce mai multe oportunități de întrerupere și rezultate vizibil mai urâte, pentru că un fragment de două litere atârnând la sfârșitul unei linii se citește ca o greșeală de tastare
Crește minimele când tipul este mare, unde fiecare fragment este vizibil de proeminent, și scade-le doar când coloana este cu adevărat îngustă și ai decis că o măsură strânsă contează mai mult decât una curată. Aceasta este o decizie de stil de casă, nu una tehnică, motiv exact pentru care este un parametru, nu o constantă
Ce înseamnă de fapt „echilibrat” aici?
Parametrul Balance schimbă comportamentul doar la sfârșitul unui pasaj. Cu echilibrarea activată, coloanele sunt scurtate la un număr exact egal de linii atunci când tot ce rămâne încape în interiorul regiunii, ceea ce oprește o pagină finală să arate două coloane pline și o a treia cu o singură linie solitară. Când pasajul nu încape, fiecare coloană își păstrează înălțimea completă, astfel încât pagina poartă cât mai mult text posibil, iar restul continuă pe pagina următoare
Această asimetrie este valoarea implicită corectă pentru documente continue. Echilibrarea la mijlocul unui articol în flux ar irosi spațiu vertical pe fiecare pagină pentru un efect cosmetic pe care nimeni nu îl vede, întrucât coloanele sunt oricum pline. Echilibrarea la final este unde ochiul chiar observă, și exact acolo se aplică
Întreruperea de linii măsoară cuvinte întregi
Algoritmul de întrerupere măsoară cuvinte complete în loc să acumuleze lățimi de caractere, și rezervă o căutare mărginită pentru token-uri supradimensionate care nu încap deloc pe o linie, cum ar fi un URL sau un număr de cont. Asta menține cazul comun rapid și cazul patologic mărginit, în loc de invers
Cratimele opționale și cele automate sunt randate doar atunci când întreruperea pe care o marchează este întreruperea care este aleasă. Asta sună evident și este un defect clasic: o implementare naivă scrie caracterul cratimă în timp ce măsoară, iar dacă întreruperea se mută, cratima rămâne în urmă la mijlocul unei linii. Nimic nu arată mai mult ca un motor de text stricat decât o cratimă rătăcită în interiorul unui cuvânt
var
Lib: TPDFlib;
Flow, Needed: Integer;
begin
// Decide layout-ul înainte de a desena orice
Flow := Lib.NewTextFlow(ArticleBody);
try
Lib.SetTextFlowLanguage(Flow, 'fr');
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
// Linii de care restul pasajului are nevoie la o lățime de coloană
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;
Păstrează setările de font identice între casete
O regulă guvernează fiecare layout bazat pe flux și merită afirmată direct: DrawTextFlow, DrawTextFlowColumns și MeasureTextFlow întrerup toate liniile folosind fontul selectat în momentul în care sunt apelate. Schimbă fontul sau dimensiunea între două casete ale aceluiași flux, sau pornește o pagină nouă fără să reselectezi unul, iar a doua casetă se întrerupe diferit față de ce a măsurat prima
Simptomul este enervant tocmai pentru că pare intermitent: text care încape pe pagina unu se revarsă pe pagina doi, sau un număr de linii măsurat nu este de acord cu ce a fost desenat. Selectează fontul o dată înainte de buclă, reselectează-l după fiecare NewPage, iar fluxul se comportă. Când apar scripturi mixte în același pasaj, rezolvarea descrisă în fallback automat de font pentru CJK și emoji se aplică atât măsurării, cât și desenării, astfel încât lățimile rămân consistente și de-a lungul run-urilor de fallback
Pentru layout-uri de raport unde fluxul este un element printre header-e, footer-e și blocuri conduse de date, tiparele de compoziție din motorul de raportare pe dataset se combină curat cu fluxurile de coloane: măsoară mai întâi, plasează mobilierul fix, apoi dă fluxului orice regiune a rămas
PDFlibPas este o bibliotecă PDF pentru Delphi, C++Builder și Lazarus, iar întregul ciclu de viață TextFlow, creare, desenare, măsurare, inspecție, derulare înapoi și eliberare, este expus și prin interfețele DLL și ActiveX. Documentația completă este pe pagina bibliotecii PDFlibPas Delphi