Teknisk artikel

PDFlibPas orddeling og balancerede tekstspalter

PDFlibPas flyder en fastholdt tekstpassage gennem én til 64 lige brede spalter med DrawTextFlowColumns, og bryder dens linjer med afgrænset sprogbevidst orddeling, når du kalder SetTextFlowLanguage og SetTextFlowHyphenation. Ni sprog understøttes, og sproget kan arves fra dokumentets Catalog-værdi /Lang i stedet for at blive sat pr. flow

Begge funktioner findes af samme grund: en smal spalte er der, hvor naiv linjebrydning holder op med at ligne typografi og begynder at ligne en fejlrapport

Hvorfor falder lige margener fra hinanden i smalle spalter?

Fordi lige margener fordeler overskydende plads i ordmellemrummene på en linje, og mængden af overskud afhænger af, hvad der kan være. I en bred spalte er overskuddet lille, og øjet lægger aldrig mærke til det. Halvér bredden, og et enkelt langt ord, der ikke passer, skubbes til næste linje, hvorved de foregående ord skal absorbere al den plads. Tre sådanne linjer i træk skaber de lodrette hvide kanaler, typografer kalder floder, og læsere oplever som tekst, der er svær at følge uden at vide hvorfor

Orddeling retter årsagen i stedet for symptomet ved at tillade et brud inde i ordet. Tyske og hollandske sammensætninger gør dette ufravigeligt: et 24-tegns substantiv i en 60-millimeter spalte har intet godt udfald uden et bruddpunkt. Engelsk tåler fraværet bedre, hvilket er grunden til, at engelsk-først-produkter ofte leverer layoutkode, der falder om, første gang en tysk kunde kører den

Hvilke sprog, og hvor kommer sproget fra?

Orddeling dækker engelsk, tysk, hollandsk, fransk, spansk, italiensk, portugisisk, russisk og tyrkisk. Sæt det eksplicit pr. flow med SetTextFlowLanguage, eller lad det arve fra dokumentets Catalog-post /Lang, som er den værdi, et tagget og tilgængeligt dokument allerede bærer

Den arv er værd at bruge frem for at tilsidesætte. Et dokument, der erklærer sit sprog i Catalog'et, fortæller skærmlæsere, søgeindekseringer og orddeling den samme kendsgerning fra ét sted, og ét sted er, hvor en kendsgerning bør bo. Hvis du allerede producerer tagget output som beskrevet i automatisk tagging til tilgængelige PDF'er, er sprogposten sat, og flowet kan blot følge den

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');
      // Aktivér, mindst 3 tegn før bruddet, 3 efter
      Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
      Lib.SetTextFlowMinLines(Flow, 2);   // strand aldrig en enkelt linje

      repeat
        // Tre spalter på tværs af et 480 pt-område, 18 pt mellemrum, balanceret
        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 og MinSuffix er typografi, ikke validering

De to heltal efter aktiveringsflaget sætter minimumsantallet af tegn, der skal blive tilbage før og efter et brud. Tre og tre er en konservativ standard, som de fleste husstile accepterer. To og to giver flere bruddmuligheder og mærkbart grimmere resultater, fordi et to-bogstavs fragment, der hænger for enden af en linje, læses som en tastefejl

Hæv minimummerne, når typografien er stor, hvor hvert fragment er visuelt fremtrædende, og sænk dem kun, når spalten reelt er smal, og du har besluttet, at et stramt mål betyder mere end et rent et. Dette er en husstilsbeslutning snarere end en teknisk en, hvilket netop er grunden til, at det er en parameter i stedet for en konstant

Hvad betyder "balanceret" egentlig her?

Parameteren Balance ændrer kun adfærd ved slutningen af en passage. Med balancering slået til afkortes spalterne til et præcist lige linjeantal, når alt det resterende passer inden for området, hvilket er det, der forhindrer en sidste side i at vise to fulde spalter og en tredje med kun én ensom linje. Når passagen ikke passer, beholder hver spalte sin fulde højde, så siden bærer så meget tekst, den kan, og resten fortsætter på næste side

Den asymmetri er det korrekte standardvalg for løbende dokumenter. Balancering midt i en flydende artikel ville spilde lodret plads på hver side for en kosmetisk effekt, ingen ser, fordi spalterne alligevel er fulde. Balancering ved slutningen er der, hvor øjet faktisk lægger mærke til det, og det er præcis der, det anvendes

Linjebrydning måler hele ord

Brydningsalgoritmen måler komplette ord i stedet for at akkumulere tegnbredder, og reserverer en afgrænset søgning til oversize-tokens, der slet ikke kan passe på en linje, såsom en URL eller et kontonummer. Det holder det almindelige tilfælde hurtigt og det patologiske tilfælde afgrænset, i stedet for omvendt

Valgfrie bløde bindestreger og automatiske bindestreger gengives kun, når det brud, de markerer, er det brud, der vælges. Det lyder indlysende og er en klassisk fejl: en naiv implementering skriver bindestregstegnet under målingen, og hvis bruddet flytter sig, bliver bindestregen tilbage midt i en linje. Intet ligner mere en ødelagt tekstmotor end en løsrevet bindestreg midt i et ord

var
  Lib: TPDFlib;
  Flow, Needed: Integer;
begin
  // Beslut layoutet, før noget tegnes
  Flow := Lib.NewTextFlow(ArticleBody);
  try
    Lib.SetTextFlowLanguage(Flow, 'fr');
    Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);

    // Antal linjer resten af passagen kræver ved én spaltebredde
    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;

Hold skrifttypeindstillingerne identiske på tværs af bokse

Én regel styrer ethvert flow-baseret layout, og det er værd at sige det ligeud: DrawTextFlow, DrawTextFlowColumns og MeasureTextFlow bryder alle linjer ved hjælp af den skrifttype, der er valgt i det øjeblik, de kaldes. Skift skrifttype eller størrelse mellem to bokse i samme flow, eller start en ny side uden at genvælge en, og den anden boks bryder anderledes end det, den første målte

Symptomet er til at blive skør af, netop fordi det ser periodisk ud: tekst, der passer på side ét, flyder over på side to, eller et målt linjeantal er uenigt med det, der blev tegnet. Vælg skrifttypen én gang før løkken, genvælg den efter hver NewPage, og flowet opfører sig korrekt. Når blandede skriftsystemer optræder i samme passage, gælder løsningen beskrevet i automatisk skrifttype-fallback til CJK og emoji-tekst for både måling og tegning, så bredder forbliver konsistente på tværs af fallback-forløbene også

For rapportlayouts, hvor flowet er ét element blandt sidehoveder, sidefødder og datadrevne blokke, kombineres sammensætningsmønstrene i dataset-rapportmotoren rent med spalteflows: mål først, placér det faste inventar, og giv derefter flowet det område, der er tilbage

PDFlibPas er et Delphi-, C++Builder- og Lazarus-PDF-bibliotek, og hele TextFlow-livscyklussen, oprettelse, tegning, måling, inspektion, tilbagespoling og frigivelse, er også eksponeret gennem DLL- og ActiveX-grænsefladerne. Fuld dokumentation findes på PDFlibPas' produktside for Delphi PDF-biblioteket