veraPDF marca ISO 19005-1 6.2.3.3 en un archivo que nunca dibuja RGB, llama incalibrado a un re f desnudo, o reprueba la página 2 mientras una página 1 idéntica pasa. PDF/A juzga esas reglas por lo que cada página pinta, así que el validador PDF/A de PDFium Component para Delphi traza cada página hasta forms, patterns, glifos y anotaciones antes de sentenciar sobre color y transparencia
Qué exigen en realidad 6.2.3.3 y 6.2.4.3
ISO 19005-1 6.2.3.3 mete tres reglas independientes en un solo párrafo. Un archivo conforme puede usar DeviceRGB o DeviceCMYK pero no debe usar ambos; si se usa cualquier espacio de color incalibrado, el archivo debe contener un OutputIntent de PDF/A-1; y DeviceRGB solo se permite con un intent cuyo perfil es RGB, DeviceCMYK solo con uno CMYK. DeviceGray queda fuera de esa lista porque la cláusula sigue explicando cómo convertir gris bajo cualquiera de los dos intents. El intent en sí es opcional: 6.2.2 dice que un archivo puede especificar uno, y solo cuenta una entrada OutputIntents con /S /GTS_PDFA1 y un stream de perfil ICC válido en /DestOutputProfile. Así que un verificador que le exige un intent a todo archivo rechaza documentos conformes que nunca pintan color de dispositivo. La Parte 1 no menciona DefaultRGB ni DefaultCMYK, y sin embargo un color remapeado a través de ellos se renderiza en un espacio basado en CIE (ISO 32000-1 §8.6.5.6), lo que satisface la exigencia de independencia de dispositivo de 6.2.3.1; la suite de pruebas de veraPDF para 6.2.3.3 lo trata como calibrado, y PDFium Component lee la cláusula igual. La prohibición de mezcla queda aparte: un archivo PDF/A-1 que pinta RGB y CMYK falla incluso cuando entradas Default calibran ambos. ISO 19005-2 6.2.4.3 suelta esa prohibición y calibra cada espacio en sus propios términos, como muestra la columna derecha de abajo, así que PDFium Component corre un escaneo por cada parte y aplica el chequeo de mezcla solo a las declaraciones PDF/A-1
| Espacio de dispositivo pintado | PDF/A-1 (ISO 19005-1 6.2.3.3) | PDF/A-2 y posteriores (ISO 19005-2 6.2.4.3) |
|---|---|---|
| DeviceRGB | DefaultRGB en alcance o un intent RGB; no junto a DeviceCMYK | DefaultRGB en alcance, o un OutputIntent RGB |
| DeviceCMYK | DefaultCMYK en alcance o un intent CMYK; no junto a DeviceRGB | DefaultCMYK en alcance, o un OutputIntent CMYK |
| DeviceGray | DefaultGray en alcance, o cualquier OutputIntent de PDF/A-1 | DefaultGray en alcance, o cualquier OutputIntent de PDF/A |
¿Qué cuenta como usar un espacio de color?
Usar un espacio de color significa pintar con él sobre algo que un lector va a renderizar. Declararlo no basta. La suite de veraPDF lo demuestra con un archivo aprobado de 6.2.3.3: un Form XObject con nombre fija CMYK con K y k, vive en el diccionario de recursos de la página, jamás es referenciado desde el contenido de la página, y el OutputIntent del archivo es RGB. Una búsqueda de nombres de color de dispositivo en todo el archivo reprueba ese archivo, y se pierde uno que pinta solo DeviceRGB bajo un intent CMYK, donde ningún par de nombres choca. PDFium Component recorre en cambio el grafo de dibujo, construido sobre su escáner preflight PDF/A a nivel de bytes. Desde cada página, el recorrido cuenta el color pintado por:
- los streams de contenido de la página, y cada Form XObject que invocan con
Do, forms anidados incluidos - un pattern seleccionado con
scnoSCN, leído con losResourcespropios del pattern - los
CharProcsde una fuente Type 3 seleccionada conTf - los image XObjects y las imágenes inline, por el espacio de color que su propio diccionario nombra
- los streams de apariencia de anotaciones, pintados encima de la página; cuando el
/AP /Nde un widget es un diccionario de estados, cada estado cuenta, porque accionar el campo pinta el que/ASno seleccionó
La mitad más sutil de la alcanzabilidad es el color que el contenido nunca nombra. q 0 0 10 10 re f Q no contiene ningún operador de color. Aun así pinta. El espacio de color inicial del estado de gráficos es DeviceGray (ISO 32000-1 Tabla 52), así que el relleno es negro DeviceGray, incalibrado bajo todas las partes salvo que exista un OutputIntent de PDF/A o un DefaultGray. El escaneo entonces lleva la cuenta de si algo ya eligió un color: rg, k, g y sus formas de trazo seleccionan un espacio de dispositivo, cs y CS seleccionan lo que nombren, y un operador de relleno, trazo o texto alcanzado antes de cualquier elección registra DeviceGray. /CS0 cs 1 0 0 /P0 scn ya eligió un espacio, así que ese scn no es gris
Las imágenes inline traen su espacio de color y sus muestras crudas dentro del propio stream de contenido, y un escáner tiene que leer ambas con cuidado. El /CS puede ser una abreviación (G, RGB, CMYK, I) o un nombre de recurso como uno que resuelve a [/Indexed /DeviceRGB 255 13 0 R], cuya base cuenta; y los bytes entre ID y EI son binario arbitrario, así que EI termina la imagen solo entre espacios en blanco, o un rg dentro de muestras comprimidas se vuelve un operador fantasma
% página 1: DefaultRGB en alcance, y un XObject que nada invoca
4 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R
/Resources << /ColorSpace << /DefaultRGB 9 0 R >>
/XObject << /Fm0 6 0 R >> >> >>
endobj
% página 2: el mismo stream de contenido, sin subdiccionario ColorSpace
7 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R /Resources << >> >>
endobj
5 0 obj << /Length 46 >> stream
1 0 0 rg 72 72 200 100 re f
q 0 0 10 10 re f Q
endstream
endobj
6 0 obj << /Type /XObject /Subtype /Form /BBox [0 0 50 50] /Length 24 >> stream
0 0 0 1 k 0 0 50 50 re f
endstream
endobj
9 0 obj [/ICCBased 10 0 R]
endobj
Cada síntoma de la apertura se deduce de este fragmento. /Fm0 jamás se encuentra con un Do, así que su k es irrelevante. El re f desnudo en ambas páginas es DeviceGray sin nada que lo calibre. El rg de la página 1 queda cubierto por el DefaultRGB en los recursos con los que pinta la página 1; la página 2 corre el stream idéntico contra recursos que carecen del mapeo, así que los mismos bytes están incalibrados ahí. Esperado para una declaración PDF/A-1b: pvaiMissingOutputIntent con pvaiMissingIccProfile, impulsados por el RGB de la página 2 y el gris de ambas páginas. El form no aporta nada; invocado, su CMYK agregaría encima la prohibición de mezcla de PDF/A-1
Los espacios de color por defecto pertenecen al diccionario de recursos que pinta
Los mapeos Default se buscan en el subdiccionario ColorSpace del diccionario de recursos actual (ISO 32000-1 §8.6.5.6), lo que los hace exactamente tan locales como los recursos mismos; un documento no tiene un default global. Un image XObject no tiene Resources propios, así que una imagen cuyo diccionario dice /ColorSpace /DeviceCMYK queda calibrada por el DefaultCMYK de la página o form que la dibuje, y la misma imagen dibujada desde dos lugares puede quedar calibrada en uno y no en el otro. Los Form XObjects, los tiling patterns y los procedimientos de glifo Type 3 cargan sus propios Resources, así que el DefaultCMYK de una página no llega a entrar en ellos, mientras que un mapeo en su propio diccionario sí. PDFium Component acota los mapeos de la misma manera. El veredicto vuelve como un record TPdfAValidationResult: la Conformance detectada más un conjunto Issues. Eso alcanza para hacer triage de una carpeta
uses
System.SysUtils, System.Classes, System.TypInfo, FPdfPdfa;
procedure ReportColorFindings(const FileName: string; Log: TStrings);
const
ColorRules: TPdfAValidationIssues = [pvaiMissingOutputIntent,
pvaiMissingIccProfile, pvaiMixedDeviceColorSpaces, pvaiBlendSpaceUndefined];
var
Source: TFileStream;
Res: TPdfAValidationResult;
Found: TPdfAValidationIssues;
Issue: TPdfAValidationIssue;
begin
Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfACompliance(Source); // lee desde la posición 0
finally
Source.Free;
end;
Log.Add(ExtractFileName(FileName) + ': ' +
GetEnumName(TypeInfo(TPdfAConformance), Ord(Res.Conformance)));
Found := Res.Issues * ColorRules;
for Issue in Found do
case Issue of
pvaiMissingOutputIntent:
Log.Add(' reachable uncalibrated device color, no PDF/A OutputIntent');
pvaiMissingIccProfile:
Log.Add(' no OutputIntent with a DestOutputProfile ICC stream');
pvaiMixedDeviceColorSpaces:
Log.Add(' RGB/CMYK use disagrees with the intent, or PDF/A-1 paints both');
pvaiBlendSpaceUndefined:
Log.Add(' transparent page without a blending color space');
end;
if Found = [] then
Log.Add(' device color and blending rules satisfied');
end;
pvaiMissingOutputIntent y pvaiMissingIccProfile aparecen juntos cuando el recorrido alcanza color de dispositivo incalibrado y no existe ningún OutputIntent de PDF/A. Un pvaiMissingIccProfile solo significa que existe un intent pero no carga perfil, cosa que 6.2.2 no acepta como intent de PDF/A. pvaiMixedDeviceColorSpaces cubre tanto la prohibición de mezcla de PDF/A-1 como un desajuste de componentes: DeviceRGB incalibrado bajo un intent de cuatro componentes, o DeviceCMYK bajo uno de tres. El conjunto nombra la regla y no la página, así que en un documento largo o fusionado, compare qué páginas declaran entradas Default y cuáles no
¿Cuándo necesita una página transparente su propio espacio de mezcla?
Una página transparente necesita su propio espacio de color de mezcla solo cuando el documento no tiene ningún OutputIntent de PDF/A. ISO 19005-2 6.2.10 (6.2.9 en PDF/A-4) convierte al intent en el espacio de color de mezcla por defecto en el sentido de ISO 32000-1 §11.3.4; sin intent, cada página que contiene transparencia debe cargar un Group cuyo diccionario de atributos (ISO 32000-1 Tabla 147) incluya CS. Ese CS también se sujeta a 6.2.4, así que un grupo de página /DeviceCMYK bajo un intent sRGB se reporta como si faltara, y PDFium Component levanta pvaiBlendSpaceUndefined para ambos; un espacio de grupo CalRGB o ICCBased no tiene tal dependencia. PDF/A-1 no llega a esta pregunta, porque ISO 19005-1 6.4 descarta la transparencia por completo
La parte difícil es la frase contiene transparencia. El Anexo A de ISO 19005-2 es normativo y prueba desde la página hacia afuera; un conteo de claves /SMask o valores /ca en todo el archivo responde otra pregunta. Las búsquedas de bytes también chocan con sintaxis de transparencia en otros lugares: /S /Trans, la acción de transición prohibida, es prefijo de cada grupo /S /Transparency, y /Screen es a la vez un subtipo de anotación prohibido y un modo de mezcla estándar, así que un nombre no significa nada hasta que se empareja con su clave. Siguiendo el Anexo A, PDFium Component trata una página como transparente cuando desde ella se alcanza cualquiera de estos:
- un ExtGState que el contenido selecciona con
gsque tenga un diccionario de soft mask, uncaoCApor debajo de 1, o un modo de mezcla distinto deNormal; uno declarado pero jamás seleccionado no cuenta - un Form XObject invocado con
Docuyo/Grouptiene/S /Transparency, o cuyo propio contenido cumple alguna de estas condiciones; un tiling pattern se juzga como un form - un image XObject con un stream
SMasko un valorSMaskInDatapor encima de 0 - una descripción de glifo de una fuente Type 3 con la que la página muestra texto, juzgada como un form
- un stream de apariencia de anotación, incluido cada estado de widget, y un modo de mezcla en el propio diccionario de la anotación
Los grupos también realimentan la regla del color de dispositivo, y aquí el corpus nos corrigió. La suite de veraPDF para 6.2.4.3 guarda archivos PDF/A-4 sin ningún OutputIntent que pintan RGB de dispositivo y se espera que pasen, porque el /Group de cada página nombra un CS calibrado. Asumimos que solo los grupos aislados podían calibrar, ya que un grupo no aislado se compone dentro de su fondo, y eso estaba mal para los grupos de página: la Tabla 147 anota que una página impuesta directamente sobre el medio de salida está efectivamente aislada sin importar I, así que su CS se honra. En un Form XObject o una apariencia de anotación el flag sí lo decide; la suite tiene un archivo de pase y uno de fallo que difieren solo en /I true. DeviceGray no toma calibración de un grupo, porque 6.2.4.3 pide DefaultGray o un intent, y un grupo cuyo CS es un espacio de dispositivo necesita calibrarse él mismo. PDFium Component por lo tanto deja que un grupo de página calibre diga lo que diga su /I, deja que un grupo de form o de apariencia calibre solo cuando está aislado, mantiene el gris fuera de la calibración por grupos, y aun así verifica un espacio de dispositivo que un grupo nombre
Un OutputIntent en lugar de un grupo en cada página
Si su propio código escribe el archivo, empiece por un OutputIntent de PDF/A a nivel de documento. Una entrada de catálogo calibra DeviceGray en todas partes, cubre el espacio de dispositivo correspondiente en cada página, y es el espacio de mezcla por defecto para cada página transparente. Los grupos de página y las entradas Default se acotan a un diccionario de recursos cada uno, y la página que una fusión posterior agrega sin ellos es la que falla. PDF 2.0 agrega OutputIntents al objeto de página (ISO 32000-2 §7.7.3.3 y §14.11.5); PDFium Component acepta ese intent a nivel de página para PDF/A-4 cuando el catálogo no tiene ninguno, mientras que las partes 1 a 3, construidas sobre PDF 1.4 a 1.7, miran solo el catálogo. Para contenido RGB, SaveAsPdfAToStream agrega un intent de catálogo construido sobre el perfil ICC sRGB incrustado del writer de PDF/A, y validar la salida muestra si el contenido de verdad era solo RGB
uses
System.SysUtils, System.Classes, PDFium, FPdfPdfa;
function ArchiveWithSrgbIntent(const InFile, OutFile: string;
out Remaining: TPdfAValidationIssues): Boolean;
var
Pdf: TPdf;
Opts: TPdfASaveOptions;
Output: TMemoryStream;
Check: TPdfAValidationResult;
begin
Result := False;
Remaining := [];
Pdf := TPdf.Create(nil);
Output := TMemoryStream.Create;
try
Pdf.FileName := InFile;
Pdf.Active := True;
if not Pdf.Active then
Exit; // no se pudo abrir el archivo
Opts := TPdfASaveOptions.Default;
Opts.Conformance := pac2b; // PDF/A-1 prohibiría la transparencia de plano
// IccProfileData queda vacío: se usa el perfil sRGB IEC61966-2.1 incorporado
if not Pdf.SaveAsPdfAToStream(Output, Opts) then
Exit;
Check := ValidatePdfACompliance(Output);
Remaining := Check.Issues;
// Esperado para contenido solo RGB: pvaiMissingOutputIntent y
// pvaiBlendSpaceUndefined desaparecen. pvaiMixedDeviceColorSpaces ahora significa
// que un camino alcanzable pinta DeviceCMYK que ningún DefaultCMYK calibra.
if Check.IsCompliant then
begin
Output.SaveToFile(OutFile);
Result := True;
end;
finally
Output.Free;
Pdf.Free;
end;
end;
Un pvaiMixedDeviceColorSpaces que sobrevive al intent sRGB es el recorrido reportando DeviceCMYK en un camino alcanzable, y ese documento necesita un mapeo DefaultCMYK en las páginas que lo pintan, o un perfil de salida CMYK, antes de que PDFium Component lo acepte como PDF/A-2b