losLab PDF Library може да вгради липсващите програми за шрифтове на вече зареден PDF с едно единствено извикване: EmbedMissingFonts преминава през всеки речник на шрифтове в документа, намира съвпадащия инсталиран системен шрифт по неговото BaseFont име и записва програмата на шрифта обратно във файла; За екипи, които поправят документи от трети страни, които не преминават PDF/A проверка поради невградени шрифтове, това е корекцията, която премахва грешката при проверка 00030
Сценарият е отчайващо често срещан; Конвейерът за въвеждане в архив получава PDF файлове от доставчици, клиенти или сканиращо бюро; документите се изобразяват добре на всяко бюро в сградата и след това PDF/A валидаторът отхвърля цялата партида с едно и също оплакване, повторено веднъж за всеки файл: поне един шрифт не е вграден; Никой нагоре по веригата няма да генерира файловете отново, така че конвейерът трябва да ги поправи; Тази статия разглежда този път на поправка; Тя е придружаваща към статията за проверка преди полет, която обхваща откриването на нарушения на PDF/A и PDF/UA: онази статия ви казва кои документи са повредени, а тази поправя най-честия начин, по който са повредени
Защо PDF/A изисква всеки шрифт да бъде вграден?
ISO 19005-1 §6.3.4 изисква всеки шрифт, използван от съответстващ документ, да носи своята програма за шрифт вътре във файла, тъй като цялото обещание на PDF/A е възпроизводимостта: документът трябва да се изобразява идентично на машина след петдесет години, която не споделя никакви шрифтове с машината, която го е произвела; Невграденият шрифт е инструкция за намиране на Arial някъде в системата за преглед, а позицията на стандарта е, че „някъде в системата за преглед“ не е гаранция за архивиране; Каквито и глифове, метрики и покритие да има заместващият шрифт, това е, което читателят получава, и то може да не е това, което авторът е видял
Историческият виновник е конвенцията Standard 14; PDF 1.0 обещаваше, че всеки визуализатор се доставя с Helvetica, Times, Courier, Symbol и ZapfDingbats, така че генераторите се научиха да препращат към тези шрифтове по име и да не вграждат нищо, и тридесет години инструменти все още правят точно това; losLab PDF Library приема изискването достатъчно сериозно, че в режим на създаване на PDF/A AddStandardFont умишлено е празна операция: библиотеката не доставя програмите за шрифтове Standard 14, не може да вгради това, което няма, и отказва да запише невградена препратка в документ, който твърди, че съответства; Тя връща 0 без избор на шрифт, така че PDF/A документът трябва вместо това да използва AddTrueTypeFont с вграждане, а всяка заявка за Embed=0 се повишава тихо до Embed=1, докато режимът PDF/A е активен; Това е от страната на записващото устройство; По-трудният проблем е от страната на четеца: документ, който някой друг вече е записал, пълен с речници на шрифтове, които вие не сте създали
Как EmbedMissingFonts поправя зареден документ?
losLab PDF Library поправя шрифтовете на място, вместо да ги изгражда отново; Когато PDF генератор записва невграден TrueType шрифт, речникът FontDescriptor, който произвежда, вече е завършен: FontName, FontBBox, Flags, Ascent, Descent, StemV, всички са налични; Единственото нещо, което го отличава от вграден шрифт, е липсата на един запис – препратката към потока /FontFile2, съдържащ действителната програма за шрифт; Така че EmbedMissingFonts не докосва речника на шрифта, кодирането, масива с ширини или който и да е поток от съдържание, който препраща към шрифта по име на ресурс; Той чете съответната програма за шрифт от системата, компресира я в нов обект поток и добавя една единствена препратка /FontFile2 (или /FontFile3 за шрифтове CIDFontType0) към FontDescriptor, който вече е там; Всичко, към което сочат страниците на документа, остава точно там, където е било, което прави операцията безопасна за изпълнение върху файлове, които не контролирате
Покритието включва и двете архитектури на шрифтове, които ще срещнете на практика: прости TrueType шрифтове и композитни Type0/CID шрифтове – вида, произвеждан за CJK текст и съвременен Unicode изход; Обхождането умишлено изброява всеки речник Font в дървото от обекти на документа, вместо да разчита на обхождане на ресурсите страница по страница, така че се взимат и шрифтове, към които се препраща от анотации или се споделят между страниците; API е едно единствено извикване към заредения документ
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');
// Обхожда всеки речник Font; връща колко шрифта
// са получили програма за шрифт. Шрифтове, чиято програма
// не може да бъде намерена в системата, се пропускат, а не се провалят.
Repaired := PDF.EmbedMissingFonts;
Writeln(Format('%d font program(s) embedded', [Repaired]));
PDF.SaveToFile('supplier-invoice-repaired.pdf');
finally
PDF.Free;
end;
end;
Една подробност, която си струва да знаете, защото обяснява защо съпоставянето на имена работи по-добре от наивно сравнение на низове: библиотеката нормализира имената на BaseFont преди търсене; Префиксите за подмножества (шаблонът ABCDEF+ от шест главни букви и знак плюс) се премахват, суфикси в PostScript стил като ArialMT се разрешават до Arial, а TrueType Collection файловете се откриват и разопаковат, така че шрифт, живеещ в .ttc, все още се вгражда правилно
Проверка на поправката с отчет преди полет
CreatePreflightReport е стъпката за проверка и цикълът е умишлено затворен: същият одит, който е отхвърлил файла, трябва да бъде този, който го одобрява; Код на грешка 00030 е констатацията от подробния одит на PDF/A, която гласи „Поне един шрифт не е вграден (FontFile/FontFile2/FontFile3 липсва)“ и тя се отчита за файла като цяло, така че един-единствен пропуснат шрифт я поддържа активна; Изпълнете отчета върху изходния файл, поправете, запазете и го изпълнете отново върху изходния резултат
function HasFontEmbeddingViolation(PDF: TPDFlib;
const FileName: string): Boolean;
var
Report: string;
begin
// ComplianceTests = 1 избира проверките за PDF/A
Report := PDF.CreatePreflightReport(FileName, '', 1, 0);
Result := Pos('00030', Report) > 0;
end;
За преглед по шрифтове, вместо присъда за целия файл, заредете отново поправения документ и избройте: FindFonts последвано от SelectFont и GetFontIsEmbedded съобщава статуса на вграждане шрифт по шрифт, което е правилният инструмент, когато групово задание трябва да запише точно кой шрифт в кой файл не е могъл да бъде поправен; Същият модел на изброяване се появява в статията за извличане на текст, изображения и шрифтове от заредени PDF файлове, където захранва извликането вместо поправката
Какво се случва, когато шрифтът не е инсталиран в системата?
EmbedMissingFonts пропуска всеки шрифт, чиято програма не може да намери, и съобщава за пропуска чрез върнатата си стойност: ако броят е по-нисък от броя на невградените шрифтове, които сте преброили, разликата е в шрифтове, които системата няма; Това е честният режим на отказ и той е по-добър от алтернативите, тъй като измислянето на заместваща програма за шрифт, посочен в документа, би променило изобразяването, което е точно това, което една архивна поправка никога не трябва да прави; За тези случаи losLab PDF Library предоставя EmbedFontProgramFromFile, който вгражда предоставена от извикващия .ttf или .otf в именования шрифт, така че конвейерът може да доставя фирмените шрифтове, които очаква да срещне, и да преминава към тях умишлено
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
// Първо опитайте с инсталирания системен шрифт, след което преминете
// към файл с шрифт, доставен заедно с приложението
if PDF.EmbedFontProgram(PDF.FontName) = 0 then
PDF.EmbedFontProgramFromFile(PDF.FontName,
'fonts\CorporateSans.ttf');
end;
end;
Две граници заслужават да бъдат заявени ясно; Първо, Type1 шрифтовете не се поправят в текущата имплементация: техният запис /FontFile изисква трисегментната PFB структура с явни клавиши за дължина и библиотеката ги пропуска, вместо да записва деформиран поток; те са редки в съвременните документи, но се появяват в стари архиви; Второ, вграждането на шрифт е лицензионен акт; Разрешенията за вграждане на TrueType шрифт принадлежат на неговата леярна и конвейер за поправка, който тъпче лицензирани програми за шрифтове в документи, напускащи организацията, трябва да има някой, който да потвърди, че лицензите за шрифтове действително го позволяват; Библиотеката ще направи това, което поискате; дали имате право да искате е въпрос към вашия правен отдел, а не към вашия компилатор
Вграждането е необходимо, но не и достатъчно
Поправянето на шрифтове изчиства грешка 00030 и нищо друго; Документ, който не преминава PDF/A поради шифроване, липсващи XMP метаданни, зависимо от устройството цветно пространство без OutputIntent или липсващи карти ToUnicode, пак ще се провали, след като всеки шрифт бъде вграден, поради което поправката принадлежи към цикъл, управляван от проверка преди полет, а не я заменя; Изпълнете пълния отчет, коригирайте това, което той посочва, и оставете отчета да ви каже кога сте готови; Има и измерение на цената: пълна програма за CJK шрифт достига мегабайти, така че вграждането на няколко от тях може драстично да раздуе малък документ; Противотежестта е подмножеството, разгледано в статията за оптимизиране на размера на PDF файлове и подмножества на шрифтове, която съкращава всяка вградена програма до глифовете, които документът действително изобразява
Предпазване на нови документи от регресия
SetEmbedAllFonts е превантивната половина на същата функция: защита от страната на записващото устройство, която спира собствения ви код от създаване на документите, които тази статия поправя; При активен SetEmbedAllFonts(1), всяко следващо извикване на AddTrueTypeFont, изискващо Embed=0, се повишава до вградена препратка, което разширява гаранцията за всеки документ, която режимът PDF/A вече налага; Той засяга шрифтове, добавени след извикването, а не шрифтове, които вече са в зареден файл, така че разделението на труда е чисто: SetEmbedAllFonts за документите, които създавате, EmbedMissingFonts за документите, които наследявате
PDF.NewDocument;
PDF.SetEmbedAllFonts(1);
// Оттук нататък AddTrueTypeFont(Name, 0) се държи
// като AddTrueTypeFont(Name, 1): няма невградена
// препратка, която да стигне до изходния файл
Двете половини, защитата от страната на записващото устройство и пътят на зареждане-поправка-запазване, са част от losLab PDF Library за Delphi, C# и VB.NET, заедно с модула за проверка преди полет, който проверява резултата; страницата на продукта съдържа пълната справочна информация за API на шрифтовете, включително извикванията за вграждане и подмножества на ниво шрифт