„losLab PDF Library“ gali įterpti trūkstamas jau įkelto PDF failo šriftų programas vienu iškvietimu: „EmbedMissingFonts“ pereina kiekvieną dokumento šriftų žodyną, randa atitinkamą įdiegtą sistemos šriftą pagal jo „BaseFont“ pavadinimą ir įrašo šrifto programą atgal į failą. Komandoms, taisančioms trečiųjų šalių dokumentus, kuriems nepavyksta atlikti PDF/A patvirtinimo dėl šriftų įterpimo, šis taisymas pašalina išankstinio patikrinimo (preflight) klaidą 00030
Šis scenarijus yra liūdnai dažnas. Archyvo apdorojimo srautas gauna PDF failus iš tiekėjų, klientų ar skenavimo biuro; dokumentai puikiai atvaizduojami visuose pastato kompiuteriuose; o tada PDF/A validatorius atmeta visą partiją su tuo pačiu skundu, pasikartojančiu kiekviename faile: bent vienas šriftas nėra įterptas. Niekas pirminiame etape failų negeneruos iš naujo, todėl srautui tenka juos taisyti. Šiame straipsnyje aptariamas šis taisymo kelias. Tai yra išankstinio patikrinimo (preflight) straipsnio, kuriame aptariamas PDF/A ir PDF/UA pažeidimų aptikimas, papildymas: ta dalis paaiškina, kurie dokumentai yra sugadinti, o ši ištaiso dažniausią jų gedimo priežastį
Kodėl PDF/A reikalauja įterpti kiekvieną šriftą?
ISO 19005-1 §6.3.4 reikalauja, kad kiekvienas reikalavimus atitinkančio dokumento naudojamas šriftas turėtų savo šrifto programą failo viduje, nes visas PDF/A pažadas yra atkuriamumas: dokumentas turi būti atvaizduojamas identiškai mašinoje po penkiasdešimties metų, kuri neturi bendrų šriftų su jį sukūrusia mašina. Neįterptas šriftas yra nurodymas ieškoti „Arial“ kažkur peržiūros sistemoje, o standarto pozicija yra ta, kad „kažkur peržiūros sistemoje“ nėra archyvavimo garantija. Kad ir kokius glifus, metriką ir aprėptį turėtų pakaitinis šriftas, būtent tai mato skaitytojas, ir tai gali skirtis nuo to, ką matė autorius
Istorinis kaltininkas yra „Standard 14“ konvencija. PDF 1.0 pažadėjo, kad kiekviena peržiūros programa turi „Helvetica“, „Times“, „Courier“, „Symbol“ ir „ZapfDingbats“, todėl generatoriai išmoko nurodyti šiuos šriftus pagal pavadinimą ir nieko neįterpti, ir trisdešimt metų įrankių vis dar daro būtent tai. „losLab PDF Library“ šį reikalavimą vertina pakankamai rimtai, todėl PDF/A kūrimo režimu AddStandardFont sąmoningai neatlieka jokių veiksmų: biblioteka neturi „Standard 14“ šriftų programų, negali įterpti to, ko neturi, ir atsisako įrašyti neįterptą nuorodą į dokumentą, kuris deklaruoja atitiktį. Ji grąžina 0 nepasirinkdama šrifto, todėl PDF/A dokumentas vietoj to turi naudoti AddTrueTypeFont su įterpimu, o bet kokia Embed=0 užklausa yra tyliai pakeičiama į Embed=1, kol yra aktyvus PDF/A režimas. Tai yra rašymo pusė. Sudėtingesnė problema yra skaitymo pusė: dokumentas, kurį jau parašė kažkas kitas, pilnas šriftų žodynų, kurių jūs nesukūrėte
Kaip „EmbedMissingFonts“ pataiso įkeltą dokumentą?
„losLab PDF Library“ taiso šriftus vietoje, o ne kuria juos iš naujo. Kai PDF generatorius įrašo neįterptą „TrueType“ šriftą, jo sukurtas „FontDescriptor“ žodynas jau yra pilnas: „FontName“, „FontBBox“, „Flags“, „Ascent“, „Descent“, „StemV“ – visi jie yra. Vienintelis dalykas, skiriantis jį nuo įterpto šrifto, yra vieno įrašo trūkumas – /FontFile2 srauto nuorodos, kurioje saugoma tikroji šrifto programa. Todėl EmbedMissingFonts neliečia šrifto žodyno, kodavimo, pločių masyvo ar bet kokio turinio srauto, kuris nurodo šriftą pagal ištekliaus pavadinimą. Ji nuskaito atitinkamą šrifto programą iš sistemos, suglaudina ją į naują srauto objektą ir prideda vienintelę /FontFile2 nuorodą (arba /FontFile3, skirtą „CIDFontType0“ šriftams) prie jau esamo „FontDescriptor“. Viskas, į ką nukreipia dokumento puslapiai, lieka tiksliai ten, kur buvo, todėl šią operaciją saugu atlikti su failais, kurių nekontroliuojate
Palaikymas apima abi šriftų architektūras, kurias sutiksite praktikoje: paprastus „TrueType“ šriftus ir sudėtinius „Type0/CID“ šriftus, kurie kuriami CJK tekstui ir moderniai „Unicode“ išvesčiai. Apėjimas sąmoningai nuskaito kiekvieną „Font“ žodyną dokumento objektų medyje, užuot kliaujantis išteklių peržiūra puslapis po puslapio, todėl aptinkami ir šriftai, nurodyti anotacijose arba bendrai naudojami keliuose puslapiuose. API yra vienas iškvietimas įkeltam dokumentui
var
PDF: TPDFlib;
Repaired: Integer;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('supplier-invoice.pdf', '') <> 1 then
raise Exception.Create('Could not load PDF');
// Walks every Font dictionary; returns how many fonts
// gained a font program. Fonts whose program cannot be
// found on the system are skipped, not failed.
Repaired := PDF.EmbedMissingFonts;
Writeln(Format('%d font program(s) embedded', [Repaired]));
PDF.SaveToFile('supplier-invoice-repaired.pdf');
finally
PDF.Free;
end;
end;
Viena detalė, kurią verta žinoti, nes ji paaiškina, kodėl pavadinimų atitikimas veikia geriau nei paprastas eilučių palyginimas: biblioteka normalizuoja „BaseFont“ pavadinimus prieš jų ieškodama. Subgrupės prefiksai (šešių didžiųjų raidžių ir pliuso ženklo šablonas ABCDEF+) yra pašalinami, „PostScript“ stiliaus priesagos, tokios kaip ArialMT, pakeičiamos į Arial, o „TrueType Collection“ failai yra aptinkami ir išpakuojami, kad .ttc faile esantis šriftas vis tiek būtų teisingai įterptas
Taisymo patikrinimas su išankstinio patikrinimo (preflight) ataskaita
CreatePreflightReport yra tikrinimo žingsnis, o ciklas yra sąmoningai uždaras: tas pats auditas, kuris pasmerkė failą, turėtų jį ir išvalyti. Klaidos kodas 00030 yra PDF/A išsamaus audito išvada, kuri skelbia: „Bent vienas šriftas nėra įterptas (trūksta FontFile/FontFile2/FontFile3)“, ir ji pateikiama visam failui, zodžiu, vienas nepastebėtas šriftas neleidžia ištaisyti klaidos. Paleiskite ataskaitą šaltinio faile, pataisykite, išsaugokite ir paleiskite ją dar kartą išvesties faile
function HasFontEmbeddingViolation(PDF: TPDFlib;
const FileName: string): Boolean;
var
Report: string;
begin
// ComplianceTests = 1 selects the PDF/A checks
Report := PDF.CreatePreflightReport(FileName, '', 1, 0);
Result := Pos('00030', Report) > 0;
end;
Norėdami gauti kiekvieno šrifto rodinį, o ne viso failo nuosprendį, vėl įkelkite pataisytą dokumentą ir atlikite numeravimą: FindFonts, po kurio seka SelectFont ir GetFontIsEmbedded, praneša apie įterpimo būseną šriftas po šrifto. Tai yra tinkamas įrankis, kai vykdant grupinį darbą reikia tiksliai užregistruoti, kurio failo kuris šriftas negalėjo būti pataisytas. Tas pats numeravimo šablonas pateikiamas straipsnyje apie teksto, vaizdų ir šriftų išgavimą iš įkeltų PDF failų, kur jis naudojamas išgavimui, o ne taisymui
Kas nutinka, kai šriftas nėra įdiegtas sistemoje?
EmbedMissingFonts praleidžia bet kurį šriftą, kurio programos nepavyksta rasti, ir praneša apie praleidimą per savo grąžinamąją vertę: jei skaičius yra mažesnis už jūsų suskaičiuotą neįterptų šriftų skaičių, skirtumas yra šriftai, kurių sistema neturi. Tai yra sąžiningas nesėkmės režimas, ir jis yra geresnis už alternatyvas, nes sukūrus pakaitinę programą dokumente nurodytam šriftui pasikeistų atvaizdavimas, o būtent to archyvinis taisymas niekada neturi daryti. Tokiais atvejais „losLab PDF Library“ teikia EmbedFontProgramFromFile, kuri įterpia kviečiančiojo pateiktą .ttf arba .otf failą į nurodytą šriftą, todėl apdorojimo srautas gali pateikti įmonės šriftus, kuriuos tikisi sutikti, ir sąmoningai grįžti prie jų
var
I, FontID: Integer;
begin
PDF.FindFonts;
for I := 1 to PDF.FontCount do
begin
FontID := PDF.GetFontID(I);
if (FontID > 0) and (PDF.SelectFont(FontID) = 1) then
if PDF.GetFontIsEmbedded = 0 then
// Try the installed system font first, then fall back
// to a font file shipped alongside the application
if PDF.EmbedFontProgram(PDF.FontName) = 0 then
PDF.EmbedFontProgramFromFile(PDF.FontName,
'fonts\CorporateSans.ttf');
end;
end;
Verta aiškiai įvardyti dvi ribas. Pirma, „Type1“ šriftai dabartiniame įgyvendinime nėra taisomi: jų /FontFile įrašui reikalinga trijų segmentų PFB struktūra su aiškiais ilgio raktais, o biblioteka juos praleidžia, užuot rašiusi netinkamo formato srautą; jie yra reti šiuolaikiniuose dokumentuose, tačiau pasitaiko senuose archyvuose. Antra, šrifto įterpimas yra licencijavimo veiksmas. „TrueType“ šrifto įterpimo leidimai priklauso jo kūrėjams, o taisymo srautas, kuris įdeda licencijuotas šriftų programas į dokumentus, paliekančius organizaciją, turėtų pasitikrinti, ar šriftų licencijos tikrai tai leidžia. Biblioteka padarys tai, ko prašote; o tai, ar galite to prašyti, yra klausimas jūsų teisės skyriui, o ne jūsų kompiuteriui
Įterpimas yra būtinas, bet nepakankamas
Šriftų taisymas ištaiso tik klaidą 00030 ir nieko daugiau. Dokumentas, kuriam nepavyksta atlikti PDF/A patikros dėl šifravimo, trūkstamų XMP metaduomenų, nuo įrenginio priklausomos spalvų erdvės be „OutputIntent“ arba trūkstamų „ToUnicode“ atvaizdų, vis tiek sugrius įterpus kiekvieną šriftą, todėl taisymas turi būti išankstinio patikrinimo ciklo dalis, o ne jį pakeisti. Paleiskite pilną ataskaitą, ištaisykite tai, ką ji nurodo, ir leiskite ataskaitai pranešti, kada baigsite. Taip pat yra išlaidų aspektas: pilna CJK šrifto programa užima megabaitus, zodžiu, kelių tokių šriftų įterpimas gali dramatiškai padidinti mažą dokumentą. Atsvara tam yra subgrupavimas (subsetting), aptariamas straipsnyje apie PDF failų dydžio optimizavimą ir šriftų subgrupavimą, kuris sumažina kiekvieną įterptą programą iki glifų, kuriuos dokumentas iš tikrųjų atvaizduoja
Apsauga nuo naujų dokumentų kokybės prastėjimo
SetEmbedAllFonts yra ta pati prevencinė funkcijos dalis: rašymo pusės apsauga, neleidžianti jūsų pačių kodui kurti dokumentų, kuriuos taiso šis straipsnis. Kai aktyvus SetEmbedAllFonts(1), bet kuris vėlesnis AddTrueTypeFont iškvietimas, prašantis Embed=0, perkeliamas į įterptąją nuorodą, o tai suteikia garantiją kiekvienam dokumentui, kurią jau užtikrina PDF/A režimas. Tai turi įtakos šriftams, pridėtiems po iškvietimo, o ne šriftams, jau esantiems įkeltame faile, todėl darbo pasidalijimas yra aiškus: SetEmbedAllFonts dokumentams, kuriuos kuriate, o EmbedMissingFonts dokumentams, kuriuos paveldite
PDF.NewDocument;
PDF.SetEmbedAllFonts(1);
// From here on, AddTrueTypeFont(Name, 0) behaves
// like AddTrueTypeFont(Name, 1): no non-embedded
// reference can reach the output file
Abi dalys, rašymo pusės apsauga ir įkėlimo-taisymo-išsaugojimo kelias, yra „losLab PDF Library“, skirto Delphi, C# ir VB.NET, dalis kartu su išankstinio patikrinimo varikliu, kuris patikrina rezultatą; produkto puslapyje pateikiama išsami šriftų API nuoroda, įskaitant kiekvieno šrifto įterpimo ir subgrupavimo iškvietimus