Techninis straipsnis

PDFlibPas: skiemenavimas ir subalansuoti stulpeliai Delphi

PDFlibPas paskirsto išsaugotą teksto ištrauką per vieną iki 64 vienodo pločio stulpelių naudodama DrawTextFlowColumns ir laužo eilutes taikydama ribotą, kalbai pritaikytą skiemenavimą, kai iškviečiate SetTextFlowLanguage ir SetTextFlowHyphenation. Palaikomos devynios kalbos, o kalba gali būti paveldėta iš dokumento katalogo /Lang reikšmės, o ne nustatoma kiekvienam srautui atskirai

Abi funkcijos egzistuoja dėl tos pačios priežasties: siauras stulpelis yra vieta, kur naivus eilučių laužymas nustoja atrodyti kaip rinkodara ir pradeda atrodyti kaip klaidos pranešimas

Kodėl lygiuotas tekstas subyra siauruose stulpeliuose?

Todėl, kad lygiavimas paskirsto likusią erpę tarp eilutės žodžių tarpų, o kiek jos lieka, priklauso nuo to, kas telpa. Plačiame matmenyje likutis mažas ir akis jo niekada nepastebi. Sumažinkite plotį per pusę, ir vienas ilgas žodis, kuris netelpa, pereina į kitą eilutę, palikdamas prieš jį buvusius žodžius sugerti visą tą erdvę. Trys tokios eilutės iš eilės sukuria vertikalius baltus ruožus, kuriuos tipografai vadina „upeliais", o skaitytojai patiria kaip tekstą, kurį sunku sekti nesuprantant kodėl

Skiemenavimas pašalina priežastį, o ne pasekmę, leisdamas laužyti žodį jo viduje. Vokiečių ir olandų sudurtiniai žodžiai tai daro neišvengiamu: 24 simbolių daiktavardis 60 milimetrų stulpelyje neturi gero sprendimo be laužymo taško. Anglų kalba geriau toleruoja šio trūkumo nebuvimą, ir būtent todėl produktai, kuriami pirmiausia anglų kalbai, dažnai išleidžia maketavimo kodą, kuris subyra vos tik su juo susiduria vokiečių kalbos klientas

Kurios kalbos ir iš kur atkeliauja kalbos nustatymas?

Skiemenavimas apima anglų, vokiečių, olandų, prancūzų, ispanų, italų, portugalų, rusų ir turkų kalbas. Nustatykite ją tiesiogiai kiekvienam srautui su SetTextFlowLanguage arba leiskite ją paveldėti iš dokumento katalogo /Lang įrašo, kuris yra reikšmė, kurią žymėtas ir prieinamas dokumentas jau turi

Šį paveldėjimą verta naudoti, o ne perrašyti. Dokumentas, kuris deklaruoja savo kalbą kataloge, tą patį faktą praneša ekrano skaitytuvams, paieškos indeksuotojams ir skiemenavimui iš vienos vietos, o viena vieta yra ten, kur faktas ir turėtų gyventi. Jei jau generuojate žymėtą išvestį, kaip aprašyta straipsnyje apie automatinį žymėjimą prieinamiems PDF dokumentams, kalbos įrašas jau nustatytas, ir srautas gali tiesiog juo remtis

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');
      // Įjungta, mažiausiai 3 simboliai prieš laužymą, 3 po jo
      Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
      Lib.SetTextFlowMinLines(Flow, 2);   // niekada nepalikti vienišos eilutės

      repeat
        // Trys stulpeliai 480 pt srityje, 18 pt tarpai, subalansuota
        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 ir MinSuffix – tai tipografija, ne validacija

Du sveikieji skaičiai po įjungimo vėliavėlės nustato mažiausią simbolių skaičių, kuris turi likti prieš laužymą ir po jo. Trys ir trys yra konservatyvi numatytoji reikšmė, kurią priima dauguma vidinių stiliaus taisyklių. Du ir du sukuria daugiau laužymo galimybių ir pastebimai bjauresnius rezultatus, nes dviejų raidžių fragmentas, kabantis eilutės gale, atrodo kaip rašybos klaida

Didinkite minimumus, kai šriftas didelis, kur kiekvienas fragmentas vizualiai ryškus, ir mažinkite juos tik tada, kai stulpelis tikrai siauras ir jūs nusprendėte, kad glaustas matmuo svarbesnis nei švarus. Tai vidinio stiliaus, o ne techninis sprendimas, ir būtent todėl tai yra parametras, o ne konstanta

Ką čia iš tikrųjų reiškia „subalansuota"?

Balance parametras keičia elgseną tik ištraukos pabaigoje. Kai balansavimas įjungtas, stulpeliai sutrumpinami iki tikslaus vienodo eilučių skaičiaus, kai visa likusi dalis telpa srityje, ir būtent tai sustabdo paskutinį puslapį nuo dviejų pilnų stulpelių ir trečio su viena vieniša eilute. Kai ištrauka netelpa, kiekvienas stulpelis išlaiko pilną aukštį, kad puslapis paneštų kuo daugiau teksto, o likutis tęsiasi kitame puslapyje

Ta asimetrija yra teisingas numatytasis elgesys tolydiems dokumentams. Balansavimas tekančio straipsnio viduryje švaistytų vertikalią erdvę kiekviename puslapyje dėl kosmetinio efekto, kurio niekas nemato, nes stulpeliai vis tiek pilni. Balansavimas pabaigoje yra vieta, kurią akis iš tikrųjų pastebi, ir būtent ten jis ir taikomas

Eilučių laužymas matuoja ištisus žodžius

Laužymo algoritmas matuoja ištisus žodžius, o ne kaupia simbolių pločius, ir rezervuoja ribotą paiešką per didelio dydžio žetonams, kurie visiškai netelpa į eilutę, tokiems kaip URL adresas ar sąskaitos numeris. Tai padeda įprastą atvejį išlaikyti greitą, o patologinį – ribotą, o ne atvirkščiai

Pasirenkamieji minkšti brūkšneliai ir automatiniai brūkšneliai atvaizduojami tik tada, kai jų pažymėtas laužymas yra tas, kuris pasirenkamas. Tai skamba akivaizdžiai ir yra klasikinis defektas: naivi realizacija įrašo brūkšnelio simbolį jau matavimo metu, ir jei laužymas pasislenka, brūkšnelis lieka eilutės viduryje. Niekas neatrodo labiau kaip sugadintas teksto variklis, nei paklydęs brūkšnelis žodžio viduryje

var
  Lib: TPDFlib;
  Flow, Needed: Integer;
begin
  // Nuspręskite maketą prieš pradedant piešti
  Flow := Lib.NewTextFlow(ArticleBody);
  try
    Lib.SetTextFlowLanguage(Flow, 'fr');
    Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);

    // Eilučių skaičius, kurio reikia likusiai ištraukai vieno stulpelio plotyje
    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;

Laikykite šrifto nustatymus vienodus visose srityse

Viena taisyklė valdo kiekvieną srautu grįstą maketą, ir ją verta pasakyti tiesiai: DrawTextFlow, DrawTextFlowColumns ir MeasureTextFlow visos laužo eilutes naudodamos tą šriftą, kuris pasirinktas jų iškvietimo metu. Pakeiskite šriftą ar dydį tarp dviejų to paties srauto sričių, arba pradėkite naują puslapį iš naujo nepasirinkę šrifto, ir antra sritis lauš eilutes kitaip nei buvo pamatuota pirmoje

Simptomas erzina būtent todėl, kad atrodo pertraukiamas: tekstas, kuris telpa pirmame puslapyje, persipildo antrame, arba pamatuotas eilučių skaičius nesutampa su tuo, kas buvo nupiešta. Pasirinkite šriftą vieną kartą prieš ciklą, pasirinkite jį iš naujo po kiekvieno NewPage, ir srautas veiks taisyklingai. Kai tame pačiame tekste sumaišyti keli raštai, straipsnyje apie automatinius atsarginius šriftus CJK ir Emoji tekstui aprašytas sprendimas taikomas tiek matavimui, tiek piešimui, todėl pločiai išlieka nuoseklūs ir atsarginio šrifto sekose

Ataskaitų maketuose, kur srautas yra vienas elementas tarp antraščių, poraščių ir duomenimis grįstų blokų, kompozicijos šablonai, aprašyti straipsnyje apie duomenų rinkinio ataskaitų variklį, dera puikiai su stulpelių srautais: pirmiausia išmatuokite, tada išdėstykite fiksuotus elementus, o srautui palikite tą sritį, kuri liko

PDFlibPas yra Delphi, C++Builder ir Lazarus PDF biblioteka, o pilnas TextFlow gyvavimo ciklas – kūrimas, piešimas, matavimas, tikrinimas, grąžinimas ir atlaisvinimas – taip pat prieinamas per DLL ir ActiveX sąsajas. Pilna dokumentacija pateikta PDFlibPas Delphi PDF bibliotekos puslapyje