Tehnični članak

PDFlibPas: samodejno nadomeščanje pisav za CJK

PDFlibPas reši znake, ki jih izbrana pisava ne zna izrisati, tako da po gručah preišče nadomestno verigo nameščenih pisav, pri tem pa ohrani oblikovanje in vrstni red dvosmernih odsekov. Funkcijo omogočite s SetAutomaticFontFallback, verigo razširite z AddFontFallback, v datoteko pa se vdelajo le tiste nadomestne pisave, ki so bile dejansko uporabljene za izpis

Težavo, ki jo to rešuje, spozna vsak izdelovalec dokumentov, ko prvič prispe ime stranke v pisavi, ki je predloga pisave nikoli ni predvidela. Napaka je tiha, zato je tudi draga

Zakaj nepodprto besedilo izgine namesto napake?

Ker PDF ne pozna pojma pisave, ki ne zna izrisati znaka. Enostavna pisava preslika bajtne kode v imena glifov prek kodiranja; sestavljena pisava preslika kode prek CMap v indekse glifov. Zahtevajte glif, ki ga pisava ne vsebuje, in dobili boste indeks glifa nič, .notdef, ki ga večina pisav izriše kot nič ali kot prazen okvirček. Datoteka je strukturno veljavna, operator besedila je pravilno oblikovan in stran se izriše. Prazno je le tam, kjer bi moralo biti ime

ISO 32000-1 od izdelovalca ne zahteva, da to opazi. Generator, ki zapiše besedilo brez preverjanja pokritosti, ustvari tehnično skladen PDF, ki je tiho izgubil vsebino, izguba pa se pokaže na zaslonu stranke šele tedne kasneje. Zato sta funkciji nadomeščanja in poročila o manjkajočih glifih izdani skupaj: reševanje tega, kar se da rešiti, je le polovica naloge, poročanje o tem, česar ni bilo mogoče rešiti, pa je druga polovica

Nadomeščanje poteka po gručah, ne po kodnih točkah

Zrnatost je podrobnost, ki ločuje delujočo izvedbo od videti-delujoče. Besedilo ni zaporedje neodvisnih znakov. Devanagarski zlog, emotikon z modifikatorjem barve kože, osnovna črka z združevalnimi znaki: vsak od njih je ena gruča, ki jo mora izrisati ena pisava, ker so odločitve o oblikovanju znotraj nje odvisne od tabel te pisave

PDFlibPas rešuje gruče, zato gručo, ki jo pokriva nadomestna pisava, v celoti izriše ta pisava. Deljenje sredi gruče, pri katerem bi polovico izrisala primarna pisava, polovico pa nadomestna, bi ustvarilo tehnično prisoten, a vidno pokvarjen rezultat, kar je verjetno slabše od praznine, s katero ste začeli. Ohranjen je tudi vrstni red poteka, zato nadomeščanje znotraj odseka od desne proti levi ne preuredi okoliškega besedila; ista mehanika je osnova navpične postavitve, opisane v navpičnem pisanju za japonščino in kitajščino

var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.SetAutomaticFontFallback(1);

    // Vrstni red iskanja: zmaga prvo ujemanje, zato širše pisave postavite na konec
    Lib.AddFontFallback('Microsoft YaHei');   // poenostavljena kitajščina
    Lib.AddFontFallback('Meiryo');            // japonščina
    Lib.AddFontFallback('Segoe UI Symbol');
    Lib.AddFontFallback('Segoe UI Emoji');

    Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_REPORT);

    Lib.AddTrueTypeFont('Arial', 1);          // 1 = vdelaj pisavo
    Lib.SetTextSize(11);
    Lib.DrawText(72, 720, 'Invoice for 北京示例科技有限公司');
    Lib.DrawText(72, 700, 'Delivery status: on time');

    Lib.SaveToFile('invoice.pdf');
  finally
    Lib.Free;
  end;
end;

Verigo uredite premišljeno. Reševanje vzame prvo pisavo, ki pokriva gručo, zato bo široka panunikodna pisava, postavljena na prvo mesto, prevzela skoraj vse, vaše skrbno izbrane pisave za posamezne pisave pa ne bodo nikoli uporabljene. Specifične pisave postavite na začetek, splošno pa na konec

Poročilo ali prekinitev: katero napako želite?

SetMissingGlyphPolicy sprejme PDF_MISSING_GLYPH_REPORT, združljivo privzeto vrednost, ali PDF_MISSING_GLYPH_ABORT. Pri politiki poročanja se operacija z besedilom nadaljuje, nerešljive kodne točke se izpustijo kot prej, vsaka pa se zabeleži. Pri politiki prekinitve je operacija z besedilom zavrnjena, še preden je zapisana kakršna koli vsebina, LastErrorCode pa je nastavljen na 521

Izbirajte glede na namen dokumenta. Serija internih poročil naj se še naprej izrisuje in beleži vrzeli, saj je nekoliko nepopolno poročilo danes boljše kot nobenega. Pravno zavezujoča pogodba, račun ali karkoli z imenom na sebi pa naj se prekine, saj je tiho izpuščen znak v imenu stranke napaka, ki jo želite odkriti v svojem procesu, ne pa v sporu. Politika prekinitve odpove pred zapisovanjem, zato za seboj ne pusti napol oblikovanega vsebinskega toka

var
  Lib: TPDFlib;
  Report: WideString;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_ABORT);
    // ... gradnja dokumenta ...

    if Lib.DrawText(72, 660, CustomerName) <> 1 then
      if Lib.LastErrorCode = PDFLIB_ERROR_MISSING_GLYPH then
      begin
        Report := Lib.GetMissingGlyphReportJSON;
        // {"valid":false,"policy":1,"eventCount":1,"events":[
        //   {"sequence":1,"documentIndex":0,"page":1,"utf16Index":12,
        //    "codePoint":21271,"unicode":"U+5317","fontName":"Arial",
        //    "fontType":"TrueType","operation":"DrawText"}]}
        EscalateToOperator(Report);
      end;
  finally
    Lib.Free;
  end;
end;

Poročilo je namenoma strojno berljivo in omejeno. Vsak dogodek nosi stran, indeks UTF-16 znotraj niza, kodno točko tako v številski kot v obliki U+XXXX, izbrano pisavo, njen tip in operacijo, pri kateri je prišlo do težave, tako da lahko podporni zahtevek poimenuje natančen znak namesto opisovanja simptoma. Sledilnik hrani zadnjih 256 dogodkov, kar zadostuje za diagnozo dokumenta in je dovolj majhno, da patološki zagon ne more spremeniti diagnostike v pomnilniško težavo

Merjenje in izris se morata ujemati

Merjenje širine uporablja iste odločitve o nadomeščanju po gručah kot izris. To se sliši samoumevno, a je stvar, pri kateri domače izdelane plasti nadomeščanja najpogosteje zgrešijo: popravijo pot izrisa, merjenje pa pustijo na primarni pisavi, zato vsako besedilno polje, desna poravnava in stolpec tabele na koncu izračunajo iz širin, ki se ne ujemajo s tem, kar je bilo izrisano

Ker si obe poti delita reševanje, niz, izmerjen pred izrisom, zavzame širino, pri kateri je bil izmerjen, vključno z nadomestnimi odseki. To je tisto, kar omogoča, da je nadomeščanje varno vklopiti povsod, ne le na mestih, ki ste jih ročno pregledali

Vdela se le tisto, kar ste uporabili

Nadomestne pisave se vdelajo lenobno: pisava v verigi, ki ni rešila nobene gruče, k izhodu ne prispeva ničesar. Dokument z enim kitajskim znakom in 5.000 latiničnimi ne nosi cele pisave CJK; nosi le tisto, kar je faza podnabora ustvarila za ta en glif, kar je vedenje, opisano v optimizaciji velikosti datoteke in podnaboru pisav

Ta lenobnost naredi široko verigo poceni za nastavitev. Registrirajte pisave, ki jih vaš nabor dokumentov morda potrebuje za vsako območje, ki ga strežete, vsak posamezen PDF pa plača le za tisto, kar je dejansko uporabil. Za dokumente, ki jih niste ustvarili sami in v katerih manjkajoče pisave že tičijo znotraj obstoječe datoteke, je popravna pot drugačna in je opisana v vdelavi manjkajočih pisav v obstoječi PDF

Eno opozorilo glede namestitve velja povedati jasno: nadomeščanje se rešuje glede na pisave, nameščene na napravi, ki poganja kodo. Strežnik brez nameščenih pisav CJK nima kam pasti nazaj, poročilo pa vam bo to sporočilo že ob prvem dokumentu in ne šele po prvi pritožbi. Priložite pisave, od katerih ste odvisni, in preverite licenco za njihovo vdelavo

PDFlibPas je knjižnica PDF za Delphi, C++Builder in Lazarus z ujemajočima se vmesnikoma DLL in ActiveX, zato sta API-ja za nadomeščanje in manjkajoče glife na voljo tudi klicateljem zunaj Pascala. Celotna dokumentacija je na strani izdelka PDFlibPas