Odborný článok

Delenie slov a vyvážené stĺpce textu v PDFlibPas

PDFlibPas naleje uchovanú pasáž do jedného až 64 stĺpcov rovnakej šírky pomocou DrawTextFlowColumns a jej riadky láme s ohraničeným jazykovo citlivým delením slov, keď zavoláte SetTextFlowLanguage a SetTextFlowHyphenation. Podporovaných je deväť jazykov a jazyk sa dá zdediť z hodnoty /Lang v Catalogu dokumentu namiesto nastavovania pre každý tok zvlášť

Obe funkcie existujú z rovnakého dôvodu: úzky stĺpec je miesto, kde naivné lámanie riadkov prestáva vyzerať ako sadzba a začína vyzerať ako hlásenie o chybe

Prečo sa zarovnaný text v úzkych stĺpcoch rozpadá?

Pretože zarovnávanie do bloku rozdeľuje zvyšný priestor do medzier medzi slovami na riadku, a to, koľko priestoru zvýši, závisí od toho, čo sa zmestí. Pri širokom stĺpci je zvyšok malý a oko si ho nikdy nevšimne. Zmenšite šírku na polovicu a jedno dlhé slovo, ktoré sa už nezmestí, sa presunie na ďalší riadok — a slová pred ním musia pohltiť celý ten priestor. Tri takéto riadky za sebou vytvárajú zvislé biele kanály, ktoré typografi nazývajú rieky, a čitatelia ich vnímajú ako text, ktorý sa ťažko sleduje, hoci nevedia prečo

Delenie slov rieši príčinu, nie symptóm, tým, že umožní zlom vo vnútri slova. Nemecké a holandské zložené slová to robia nevyhnutným: 24-znakové podstatné meno v 60-milimetrovom stĺpci nemá dobrý výsledok bez miesta na zlom. Angličtina absenciu delenia znáša lepšie, a preto produkty orientované najprv na angličtinu často dodávajú kód rozloženia, ktorý sa zrúti hneď pri prvom nemeckom zákazníkovi

Ktoré jazyky, a odkiaľ sa jazyk berie?

Delenie slov pokrýva angličtinu, nemčinu, holandčinu, francúzštinu, španielčinu, taliančinu, portugalčinu, ruštinu a turečtinu. Nastavte ho explicitne pre každý tok pomocou SetTextFlowLanguage, alebo ho nechajte zdediť z položky /Lang v Catalogu dokumentu — hodnoty, ktorú označený a prístupný dokument už aj tak nesie

Toto dedenie sa oplatí využívať namiesto prepisovania. Dokument, ktorý deklaruje svoj jazyk v Catalogu, oznamuje čítačkám obrazovky, vyhľadávacím indexerom aj deleniu slov ten istý fakt z jedného miesta, a jedno miesto je presne tam, kde má fakt žiť. Ak už produkujete označený výstup opísaný v článku automatické značkovanie prístupných PDF, položka jazyka je nastavená a tok ju môže jednoducho nasledovať

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');
      // Zapnúť, aspoň 3 znaky pred zlomom, 3 za ním
      Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
      Lib.SetTextFlowMinLines(Flow, 2);   // nikdy nenechať osamotený jeden riadok

      repeat
        // Tri stĺpce v oblasti 480 pt, 18 pt medzery, vyvážené
        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 a MinSuffix sú typografia, nie validácia

Dve celé čísla za príznakom zapnutia určujú minimálny počet znakov, ktoré musia zostať pred zlomom a za ním. Tri a tri je konzervatívna predvolená hodnota, ktorú akceptuje väčšina firemných štýlov. Dve a dve dáva viac možností na zlom a citeľne škaredšie výsledky, pretože dvojpísmenový zvyšok visiaci na konci riadku pôsobí ako preklep

Minimá zvýšte, keď je písmo veľké, kde je každý zvyšok vizuálne nápadný, a znížte ich iba vtedy, keď je stĺpec naozaj úzky a rozhodli ste sa, že tesná sadzba je dôležitejšia než čistá. Ide o rozhodnutie firemného štýlu, nie technické — a presne preto je to parameter, nie konštanta

Čo tu vlastne znamená „vyvážené“?

Parameter Balance mení správanie len na konci pasáže. Keď je vyvažovanie zapnuté, stĺpce sa skrátia na presne rovnaký počet riadkov vtedy, keď sa všetko zvyšné zmestí do danej oblasti — a práve to zabraňuje tomu, aby posledná strana ukázala dva plné stĺpce a tretí s jedným osamoteným riadkom. Keď sa pasáž nezmestí, každý stĺpec si ponechá plnú výšku, takže strana nesie čo najviac textu a zvyšok pokračuje na ďalšej strane

Táto asymetria je správna predvoľba pre plynulé dokumenty. Vyvažovanie uprostred plynúceho článku by na každej strane plytvalo zvislým priestorom kvôli kozmetickému efektu, ktorý si nikto nevšimne, keďže stĺpce sú aj tak plné. Vyvažovanie na konci je miesto, kde si to oko naozaj všimne, a presne tam sa aj uplatňuje

Lámanie riadkov meria celé slová

Algoritmus lámania meria celé slová namiesto sčítavania šírok jednotlivých znakov a vyhradzuje ohraničené hľadanie pre nadmerne dlhé tokeny, ktoré sa na riadok vôbec nezmestia, ako je URL adresa alebo evidenčné číslo. Vďaka tomu je bežný prípad rýchly a patologický prípad ohraničený — nie naopak

Voliteľné mäkké spojovníky a automatické spojovníky sa vykreslia iba vtedy, keď zlom, ktorý označujú, je aj skutočne vybraný zlom. Znie to samozrejme a ide o klasickú chybu: naivná implementácia zapíše znak spojovníka už počas merania, a ak sa zlom presunie, spojovník ostane visieť uprostred riadku. Nič nevyzerá viac ako pokazený textový engine než zablúdený spojovník uprostred slova

var
  Lib: TPDFlib;
  Flow, Needed: Integer;
begin
  // Rozhodnite o rozložení skôr, než čokoľvek nakreslíte
  Flow := Lib.NewTextFlow(ArticleBody);
  try
    Lib.SetTextFlowLanguage(Flow, 'fr');
    Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);

    // Počet riadkov, ktoré zvyšok pasáže potrebuje pri šírke jedného stĺpca
    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;

Udržujte nastavenia písma identické naprieč rámčekmi

Jedno pravidlo riadi každé rozloženie založené na toku a stojí za to povedať ho na rovinu: DrawTextFlow, DrawTextFlowColumns aj MeasureTextFlow lámu riadky pomocou písma, ktoré je vybrané v okamihu ich volania. Zmeňte písmo alebo veľkosť medzi dvoma rámčekmi toho istého toku, alebo začnite novú stranu bez opätovného výberu písma, a druhý rámček sa zalomí inak, než čo zmeral prvý

Príznak je znepokojivý práve preto, že vyzerá nesúvislo: text, ktorý sa zmestí na prvú stranu, na druhej pretečie, alebo zmeraný počet riadkov nesúhlasí s tým, čo sa nakreslilo. Vyberte písmo raz pred slučkou, znovu ho vyberte po každom NewPage, a tok sa bude správať predvídateľne. Keď sa v jednej pasáži zmiešajú viaceré písma, riešenie opísané v článku automatické náhradné písma pre CJK a emoji text platí rovnako pre meranie aj kreslenie, takže šírky ostávajú konzistentné aj naprieč náhradnými behmi

Pri rozloženiach reportov, kde je tok len jedným prvkom medzi hlavičkami, pätami a blokmi riadenými dátami, sa vzory skladby z článku report engine nad datasetom čisto kombinujú so stĺpcovými tokmi: najprv zmerajte, umiestnite pevné prvky a tok potom dostane akúkoľvek zvyšnú oblasť

PDFlibPas je knižnica PDF pre Delphi, C++Builder a Lazarus a celý životný cyklus TextFlow — vytvorenie, kreslenie, meranie, inšpekcia, návrat na začiatok a uvoľnenie — je dostupný aj cez rozhrania DLL a ActiveX. Úplná dokumentácia je na stránke PDFlibPas Delphi PDF library