Műszaki cikk

Hiányzó betűtípusok beágyazása meglévő PDF-ekbe a PDF/A szabványhoz Delphi-ben

A losLab PDF Library egyetlen hívással képes beágyazni egy már betöltött PDF hiányzó betűtípus-programjait: az EmbedMissingFonts végigmegy a dokumentum összes betűtípus-szótárán, a BaseFont neve alapján megkeresi a megfelelő telepített rendszer-betűtípust, és visszaírja a betűtípus-programot a fájlba; az olyan csapatok számára, amelyek harmadik féltől származó, a betűtípusok beágyazása miatt a PDF/A ellenőrzésen elbukó dokumentumokat javítanak, ez a javítás megszünteti a preflight 00030-as hibát

A forgatókönyv elszomorítóan gyakori; egy archívum-beviteli folyamat PDF-eket fogad a beszállítóktól, ügyfelektől vagy egy szkennelő irodától; a dokumentumok a szervezet minden asztalán megfelelően jelennek meg; majd a PDF/A ellenőrző elutasítja a teljes köteget, fájlonként ugyanazt a panaszt ismételve: legalább egy betűtípus nincs beágyazva; a forrásnál senki sem fogja újra előállítani a fájlokat, így a feldolgozó rendszernek kell kijavítania azokat; ez a cikk ezt a javítási utat tárgyalja; ez a társa a preflight cikknek, amely a PDF/A és PDF/UA szabálysértések észlelését mutatja be: az a cikk elmondja, mely dokumentumok hibásak, ez pedig kijavítja a leggyakoribb hibájukat

Miért követeli meg a PDF/A, hogy minden betűtípus be legyen ágyazva?

Az ISO 19005-1 §6.3.4 megköveteli, hogy a megfelelő dokumentum által használt összes betűtípus hordozza a betűtípus-programját a fájlon belül, mivel a PDF/A teljes ígérete a reprodukálhatóság: a dokumentumnak pontosan ugyanúgy kell megjelennie egy ötven év múlva használt gépen is, amelyen nincsenek közös betűtípusok a dokumentumot előállító géppel; a be nem ágyazott betűtípus egy utasítás arra, hogy keresse meg az Arial-t valahol a megjelenítő rendszeren, a szabvány álláspontja szerint pedig a „valahol a megjelenítő rendszeren” nem jelent archiválási garanciát; bármilyen karakterekkel, metriákokkal és lefedettséggel is rendelkezik a helyettesítő betűtípus, az olvasó azt fogja kapni, és előfordulhat, hogy ez nem egyezik meg azzal, amit a szerző látott

A történelmi bűnös a Standard 14 konvenció; a PDF 1.0 azt ígérte, hogy minden megjelenítő tartalmazza a Helvetica, Times, Courier, Symbol és ZapfDingbats betűtípusokat, így a generátorok megtanulták ezeket a betűtípusokat név szerint hivatkozni és semmit sem beágyazni, és a harminc éves eszközök még mindig pontosan ezt teszik; a losLab PDF Library elég komolyan veszi a követelményt ahhoz, hogy PDF/A létrehozási módban az AddStandardFont szándékosan ne végezzen műveletet: a könyvtár nem tartalmazza a Standard 14 betűtípus-programokat, nem tudja beágyazni azt, amivel nem rendelkezik, és megtagadja a be nem ágyazott hivatkozások beírását a megfelelőséget állító dokumentumba; 0 értéket ad vissza betűtípus kiválasztása nélkül, így a PDF/A dokumentumnak helyette az AddTrueTypeFont hívást kell használnia beágyazással, és minden Embed=0 kérés csendben Embed=1-re minősül át, amíg a PDF/A mód aktív; ez az író oldala; a nehezebb probléma az olvasó oldala: egy dokumentum, amelyet valaki más már megírt, tele olyan betűtípus-szótárakkal, amelyeket nem ön hozott létre

Hogyan javítja ki az EmbedMissingFonts a betöltött dokumentumot?

A losLab PDF Library helyben javítja a betűtípusokat, ahelyett, hogy újjáépítené őket; amikor egy PDF generátor be nem ágyazott TrueType betűtípust ír, az általa létrehozott FontDescriptor szótár már teljes: FontName, FontBBox, Flags, Ascent, Descent, StemV, mind jelen vannak; az egyetlen dolog, ami elválasztja egy beágyazott betűtípustól, egyetlen bejegyzés hiánya, a /FontFile2 adatfolyam-hivatkozás, amely a tényleges betűtípus-programot tartalmazza; így az EmbedMissingFonts nem nyúl a betűtípus-szótárhoz, a kódoláshoz, a szélességtömbhöz vagy bármely olyan tartalomfolyamhoz, amely erőforrásnév alapján hivatkozik a betűtípusra; beolvassa a megfelelő betűtípus-programot a rendszerből, tömöríti azt egy új adatfolyam-objektumba, and egyetlen /FontFile2 hivatkozást (vagy CIDFontType0 betűtípusok esetén /FontFile3-at) fűz a már meglévő FontDescriptor-hoz; minden, amire a dokumentum oldalai mutatnak, pontosan ott marad, ahol volt, ami biztonságossá teszi a művelet futtatását olyan fájlokon is, amelyeket nem ön irányít

A lefedettség magában foglalja mindkét betűtípus-architektúrát, amellyel a gyakorlatban találkozni fog: az egyszerű TrueType betűtípusokat és az összetett Type0/CID betűtípusokat, amelyeket a CJK szövegekhez és a modern Unicode kimenetekhez használnak; a bejárás szándékosan felsorolja a dokumentum objektumfájában lévő összes Font szótárt, ahelyett, hogy az oldalak szerinti erőforrás-bejárásra támaszkodna, így a megjegyzésekből hivatkozott vagy az oldalak között megosztott betűtípusokat is felveszi; az API egyetlen hívás a betöltött dokumentumon

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');

    // Végigmegy az összes Font szótáron; visszaadja, hány betűtípus
    // kapott betűtípus-programot; azokat a betűtípusokat, amelyek programja nem
    // található a rendszeren, kihagyja, nem okoz hibát
    Repaired := PDF.EmbedMissingFonts;
    Writeln(Format('%d font program(s) embedded', [Repaired]));

    PDF.SaveToFile('supplier-invoice-repaired.pdf');
  finally
    PDF.Free;
  end;
end;

Egy részlet, amit érdemes tudni, mert megmagyarázza, miért működik jobban a névillesztés, mint egy egyszerű karakterlánc-összehasonlítás: a könyvtár normalizálja a BaseFont neveket a keresésük előtt; a részhalmaz-előtagokat (a hat nagybetűből és egy pluszjelből álló ABCDEF+ mintát) eltávolítja, a PostScript-stílusú utótagokat, mint például az ArialMT, az Arial névre oldja fel, a TrueType Collection fájlokat pedig észleli és kicsomagolja, így a .ttc fájlon belül található betűkészlet is megfelelően beágyazódik

A javítás ellenőrzése preflight jelentéssel

A CreatePreflightReport az ellenőrzési lépés, és a kör szándékosan bezárul: ugyanannak a vizsgálatnak kell tisztáznia a fájlt, amely elítélte azt; a 00030-as hibakód a PDF/A mélyreható vizsgálatának azon megállapítása, amely szerint „Legalább egy betűtípus nincs beágyazva (a FontFile/FontFile2/FontFile3 hiányzik)”, és ezt a teljes fájlra vonatkozóan jelenti, így egyetlen figyelmen kívül hagyott betűtípus is fenntartja azt; futtassa a jelentést a forrásfájlon, javítsa ki, mentse el, majd futtassa újra a kimeneten

function HasFontEmbeddingViolation(PDF: TPDFlib;
  const FileName: string): Boolean;
var
  Report: string;
begin
  // A ComplianceTests = 1 kiválasztja a PDF/A ellenőrzéseket
  Report := PDF.CreatePreflightReport(FileName, '', 1, 0);
  Result := Pos('00030', Report) > 0;
end;

A fájlonkénti döntés helyett a betűtípusonkénti nézethez töltse be újra a javított dokumentumot, és listázza ki: a FindFonts, amelyet a SelectFont és a GetFontIsEmbedded követ, betűtípusonként jelenti a beágyazás állapotát, ami a megfelelő eszköz, ha egy kötegelt feladatnak pontosan naplóznia kell, hogy melyik fájlban melyik betűtípus nem volt javítható; ugyanez a felsorolási minta jelenik meg a szövegek, képek és betűtípusok betöltött PDF-ekből történő kinyeréséről szóló cikkben is, ahol a javítás helyett a kinyerést szolgálja

Mi történik, ha a betűtípus nincs telepítve a rendszerre?

Az EmbedMissingFonts kihagy minden olyan betűtípust, amelynek programját nem találja, és a visszatérési értékén keresztül jelzi a kihagyást: ha a kapott szám alacsonyabb, mint a megszámolt be nem ágyazott betűtípusok száma, a különbséget a rendszerben nem található betűtípusok jelentik; ez a korrekt hibaállapot, és jobb, mint az alternatívák, mert a dokumentumban megnevezett betűtípus helyettesítő programjának kitalálása megváltoztatná a megjelenítést, amit egy archiválási javításnak soha nem szabad megtennie; ezekre az esetekre a losLab PDF Library az EmbedFontProgramFromFile függvényt kínálja, amely a hívó által biztosított .ttf vagy .otf fájlt ágyazza be a megadott betűtípusba, így a feldolgozási folyamat tartalmazhatja a várhatóan előforduló vállalati betűtípusokat, és szándékosan vissza tud lépni ezekre

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
        // Először próbálja meg a telepített rendszer-betűtípust, majd lépjen vissza
        // az alkalmazás mellett szállított betűtípus-fájlra
        if PDF.EmbedFontProgram(PDF.FontName) = 0 then
          PDF.EmbedFontProgramFromFile(PDF.FontName,
            'fonts\CorporateSans.ttf');
  end;
end;

Két korlátot érdemes egyértelműen kimondani; először is, a Type1 betűtípusok nem javíthatók a jelenlegi megvalósításban: a /FontFile bejegyzésükhöz a három szegmensből álló, explicit hosszúságú kulcsokkal rendelkező PFB struktúra szükséges, és a könyvtár kihagyja őket, ahelyett, hogy hibás adatfolyamot írna; ezek ritkák a modern dokumentumokban, de megjelennek a régi archívumokban; másodszor, a betűtípus beágyazása licencelési aktus; a TrueType betűtípus beágyazási engedélyei a készítő tulajdonában vannak, és egy olyan javítási folyamat esetében, amely licencelt betűtípus-programokat helyez el a szervezetet elhagyó dokumentumokba, valakinek meg kell erősítenie, hogy a betűtípus-licencek valóban megengedik ezt; a könyvtár megteszi, amit kér; hogy kérheti-e, az a jogi osztályának a kérdése, nem a fordítójáé

A beágyazás szükséges, de nem elégséges

A betűtípusok javítása a 00030-as hibát oldja meg, és semmi mást; egy olyan dokumentum, amely a PDF/A ellenőrzésen a titkosítás, a hiányzó XMP metaadatok, az OutputIntent nélküli eszközfüggő színtér vagy a hiányzó ToUnicode leképezések miatt megbukik, továbbra is meg fog bukni az összes betűtípus beágyazása után is, ezért a javításnak egy preflight-vezérelt ciklusban van a helye, nem pedig annak helyettesítéseként; futtassa a teljes jelentést, javítsa ki, amit megnevez, és hagyja, hogy a jelentés megmondja, mikor végzett; költségdimenzió is létezik: egy teljes CJK betűtípus-program több megabájtos is lehet, így több ilyen beágyazása drámaian megnövelheti a kis dokumentum méretét; az ellensúlyt a részhalmazok (subsetting) képzése jelenti, amelyről a PDF-fájlméret optimalizálásáról és a betűtípusok részhalmaz-képzéséről szóló cikkben olvashat, ez lecsökkenti a beágyazott programokat a dokumentum által ténylegesen megjelenített karakterekre

Új dokumentumok visszaesésének megakadályozása

A SetEmbedAllFonts ugyanennek a funkciónak a megelőzési oldala: egy író oldali védelem, amely megakadályozza, hogy a saját kódja olyan dokumentumokat hozzon létre, amelyeket ez a cikk javít; aktív SetEmbedAllFonts(1) mellett a későbbi, Embed=0 értéket kérő AddTrueTypeFont hívások beágyazott hivatkozássá minősülnek át, ami kiterjeszti minden dokumentumra azt a garanciát, amelyet a PDF/A mód már kikényszerít; ez a hívás után hozzáadott betűtípusokra van hatással, nem a már betöltött fájlban lévőkre, így a munkamegosztás tiszta: a SetEmbedAllFonts az ön által létrehozott dokumentumokhoz, az EmbedMissingFonts pedig az örökölt dokumentumokhoz használható

PDF.NewDocument;
PDF.SetEmbedAllFonts(1);
// Innentől kezdve az AddTrueTypeFont(Name, 0) úgy viselkedik,
// mint az AddTrueTypeFont(Name, 1): nem kerülhet be
// be nem ágyazott hivatkozás a kimeneti fájlba

Mindkét oldal, az író oldali védelem és a betöltés-javítás-mentés út is része a Delphi, C# és VB.NET rendszerekhez készült losLab PDF Library terméknek, a vizsgálatot végző preflight motorral együtt; a termékoldal tartalmazza a teljes betűtípus-API referenciát, beleértve a betűtípusonkénti beágyazási és részhalmaz-képzési hívásokat is