losLab PDF Library puede incrustar los programas de fuentes faltantes de un PDF ya cargado con una sola llamada: EmbedMissingFonts recorre cada diccionario de fuentes en el documento, localiza la fuente del sistema instalada correspondiente por su nombre BaseFont y escribe el programa de fuentes de nuevo en el archivo. Para los equipos que reparan documentos de terceros que fallan en la validación de PDF/A debido a la incrustación de fuentes, esta es la solución que hace desaparecer el error de verificación preliminar 00030
El escenario es preocupantemente común. Una canalización de ingesta de archivos recibe archivos PDF de proveedores, clientes o una oficina de escaneo; los documentos se representan bien en todos los escritorios del edificio; y luego el validador de PDF/A rechaza todo el lote con la misma queja repetida una vez por archivo: al menos una fuente no está incrustada. Nadie en el origen regenerará los archivos, por lo que la canalización tiene que repararlos. Este artículo cubre esa ruta de reparación. Es el complemento del artículo de verificación preliminar, que cubre la detección de infracciones de PDF/A y PDF/UA: ese texto le indica qué documentos están dañados y este repara la forma más frecuente en la que están dañados
¿Por qué PDF/A requiere que se incrusten todas las fuentes?
La norma ISO 19005-1 §6.3.4 exige que cada fuente utilizada por un documento conforme lleve su programa de fuentes dentro del archivo, porque toda la promesa de PDF/A es la reproducibilidad: el documento debe representarse de manera idéntica en una máquina dentro de cincuenta años que no comparta ninguna fuente con la máquina que lo produjo. Una fuente no incrustada es una instrucción para ir a buscar Arial en algún lugar del sistema de visualización, y la posición de la norma es que "en algún lugar del sistema de visualización" no es una garantía de archivo. Cualesquiera que sean los glifos, las métricas y la cobertura que tenga la fuente de reemplazo, eso es lo que obtiene el lector, y puede que no sea lo que vio el autor
El culpable histórico es la convención Estándar 14. PDF 1.0 prometía que cada visor incluiría Helvetica, Times, Courier, Symbol y ZapfDingbats, por lo que los generadores aprendieron a hacer referencia a esas fuentes por su nombre y no incrustar nada, y treinta años de herramientas todavía hacen exactamente eso. losLab PDF Library se toma este requisito lo suficientemente en serio como para que en el modo de creación de PDF/A, AddStandardFont sea deliberadamente una operación sin efecto: la biblioteca no incluye los programas de fuentes Estándar 14, no puede incrustar lo que no tiene y se niega a escribir una referencia no incrustada en un documento que declare conformidad. Devuelve 0 sin seleccionar una fuente, por lo que un documento PDF/A debe usar AddTrueTypeFont con incrustación en su lugar, y cualquier solicitud de Embed=0 se promueve silenciosamente a Embed=1 mientras el modo PDF/A esté activo. Esa es la parte del escritor. El problema más difícil es la parte del lector: un documento que alguien más ya escribió, lleno de diccionarios de fuentes que usted no creó
¿Cómo repara EmbedMissingFonts un documento cargado?
losLab PDF Library repara las fuentes en su lugar en lugar de reconstruirlas. Cuando un generador de PDF escribe una fuente TrueType no incrustada, el diccionario FontDescriptor que produce ya está completo: FontName, FontBBox, Flags, Ascent, Descent, StemV, todos presentes. Lo único que lo separa de una fuente incrustada es la ausencia de una entrada, la referencia de flujo /FontFile2 que contiene el programa de fuente real. Por lo tanto, EmbedMissingFonts no toca el diccionario de fuentes, la codificación, la matriz de anchos ni ningún flujo de contenido que haga referencia a la fuente por su nombre de recurso. Lee el programa de fuentes coincidente del sistema, lo comprime en un nuevo objeto de flujo y añade una única referencia /FontFile2 (o /FontFile3 para fuentes CIDFontType0) al FontDescriptor que ya está allí. Todo lo que apuntan las páginas del documento permanece exactamente donde estaba, lo que hace que la operación sea segura de ejecutar en archivos que usted no controla
La cobertura incluye ambas arquitecturas de fuentes que encontrará en la práctica: fuentes TrueType simples y fuentes compuestas Type0/CID, del tipo producido para texto CJK y salida Unicode moderna. El recorrido enumera deliberadamente cada diccionario de fuentes en el árbol de objetos del documento en lugar de depender de un recorrido de recursos página por página, por lo que las fuentes a las que se hace referencia desde las anotaciones o que se comparten entre páginas también se recopilan. La API es una sola llamada en el documento cargado
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;
Un detalle que vale la pena conocer porque explica por qué la coincidencia de nombres funciona mejor que una simple comparación de cadenas: la biblioteca normaliza los nombres BaseFont antes de buscarlos. Los prefijos de subconjuntos (el patrón ABCDEF+ de seis letras mayúsculas y un signo más) se eliminan, los sufijos de estilo PostScript como ArialMT se resuelven como Arial, y los archivos TrueType Collection se detectan y desempaquetan para que una tipografía que viva dentro de un .ttc se incruste correctamente
Verificación de la reparación con un informe de verificación preliminar
CreatePreflightReport es el paso de verificación y el bucle se cierra deliberadamente: la misma auditoría que condenó el archivo debería ser la que lo apruebe. El código de error 00030 es el hallazgo de la auditoría profunda de PDF/A que dice "Al menos una fuente no está incrustada (falta FontFile/FontFile2/FontFile3)", y se notifica sobre el archivo en su totalidad, por lo que una sola fuente pasada por alto lo mantiene activo. Ejecute el informe en el archivo de origen, repárelo, guárdelo y ejecútelo de nuevo en el de salida
Para obtener una vista por fuente en lugar de un veredicto por archivo, cargue el documento reparado nuevamente y enumere: FindFonts seguido de SelectFont y GetFontIsEmbedded informa el estado de incrustación fuente por fuente, que es la herramienta adecuada cuando un trabajo por lotes necesita registrar exactamente qué tipografía en qué archivo no se pudo reparar. El mismo patrón de enumeración aparece en el artículo sobre la extracción de texto, imágenes y fuentes de archivos PDF cargados, donde sirve para la extracción en lugar de para la reparación
¿Qué sucede cuando la fuente no está instalada en el sistema?
EmbedMissingFonts omite cualquier fuente cuyo programa no pueda encontrar e informa la omisión a través de su valor de retorno: si el recuento resulta ser inferior al número de fuentes no incrustadas que contó, la diferencia son fuentes que el sistema no tiene. Este es el modo de fallo honesto y es mejor que las alternativas, porque inventar un programa sustituto para una fuente con nombre en el documento cambiaría la representación, que es precisamente lo que una reparación de archivo nunca debe hacer. Para estos casos, losLab PDF Library proporciona EmbedFontProgramFromFile, que incrusta un .ttf o .otf proporcionado por el llamador en la fuente con nombre, de modo que una canalización pueda distribuir las fuentes corporativas que espera encontrar y recurrir a ellas deliberadamente
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;
Dos límites merecen ser expuestos claramente. Primero, las fuentes Type1 no se reparan en la implementación actual: su entrada /FontFile requiere la estructura PFB de tres segmentos con claves de longitud explícitas, y la biblioteca las omite en lugar de escribir un flujo mal formado; son raras en los documentos modernos pero aparecen en archivos antiguos. Segundo, incrustar una fuente es un acto de licencia. Los permisos de incrustación de una fuente TrueType pertenecen a su fundición, y una canalización de reparación que introduce programas de fuentes con licencia en documentos que salen de la organización debería hacer que alguien confirme que las licencias de las fuentes realmente lo permiten. La biblioteca hará lo que le pida; si puede pedirlo es una pregunta para su departamento legal, no para su compilador
La incrustación es necesaria, no suficiente
Reparar las fuentes borra el error 00030 y nada más. Un documento que falla en PDF/A debido al cifrado, a la falta de metadatos XMP, a un espacio de color dependiente del dispositivo sin un OutputIntent o a la ausencia de mapas ToUnicode seguirá fallando después de incrustar cada fuente, por lo que la reparación debe formar parte de un bucle impulsado por la verificación preliminar en lugar de reemplazarlo. Ejecute el informe completo, corrija lo que nombre y deje que el informe le indique cuándo ha terminado. También hay una dimensión de coste: un programa de fuentes CJK completo puede alcanzar los megabytes, por lo que incrustar varios de ellos puede inflar drásticamente un documento pequeño. El contrapeso es la subdivisión (subsetting), cubierta en el artículo sobre la optimización del tamaño del archivo PDF y la subdivisión de fuentes, que reduce cada programa incrustado a los glifos que el documento realmente representa
Evitar que los nuevos documentos retrocedan
SetEmbedAllFonts es la mitad preventiva de la misma característica: una protección en la parte del escritor que evita que su propio código produzca los documentos que este artículo repara. Con SetEmbedAllFonts(1) activo, cualquier llamada posterior a AddTrueTypeFont que solicite Embed=0 se promueve a una referencia incrustada, lo que extiende a cada documento la garantía que el modo PDF/A ya impone. Afecta a las fuentes añadidas después de la llamada, no a las fuentes que ya están en un archivo cargado, por lo que la división del trabajo es clara: SetEmbedAllFonts para los documentos que cree, EmbedMissingFonts para los documentos que herede
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
Ambas mitades, la protección en la parte del escritor y la ruta de carga-reparación-guardado, forman parte de losLab PDF Library para Delphi, C# y VB.NET, junto con el motor de verificación preliminar que verifica el resultado; la página del producto contiene la referencia completa de la API de fuentes, incluidas las llamadas de incrustación y subdivisión por fuente