veraPDF marca ISO 19005-1 6.2.3.3 en un archivo que nunca dibuja RGB, da por sin calibrar un re f pelado, o falla 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 las formas, los patrones, los glifos y las anotaciones antes de pronunciarse sobre color y transparencia
Qué exigen realmente 6.2.3.3 y 6.2.4.3
ISO 19005-1 6.2.3.3 empaqueta tres reglas independientes en un solo párrafo. Un archivo conforme puede usar DeviceRGB o DeviceCMYK pero no deberá usar ambos; si se usa algún espacio de color sin calibrar, el archivo deberá contener un OutputIntent de PDF/A-1; y DeviceRGB solo se permite con un intent cuyo perfil sea RGB, DeviceCMYK solo con uno CMYK. DeviceGray queda fuera de esa lista porque la cláusula sigue explicando la conversión de gris bajo cualquier intent. 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 comprobador que exige un intent a cada 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 tests de veraPDF para 6.2.3.3 lo trata como calibrado, y PDFium Component lee la cláusula igual. La prohibición de mezclas va por su cuenta: un archivo PDF/A-1 que pinta RGB y CMYK falla incluso cuando entradas Default calibran ambos. ISO 19005-2 6.2.4.3 quita esa prohibición y calibra cada espacio por sus propios términos, como muestra la columna derecha de abajo, así que PDFium Component ejecuta un escaneo por cada parte y aplica la comprobación 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 en un trazado que un lector va a renderizar. Declararlo no basta. La suite de veraPDF lo demuestra con un archivo que pasa 6.2.3.3: un Form XObject nombrado fija CMYK con K y k, vive en el diccionario de recursos de la página, nunca se referencia desde el contenido de página, y el OutputIntent del archivo es RGB. Una búsqueda de nombres de color de dispositivo en todo el archivo falla ese archivo, y se pierde uno que solo pinta 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 de 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 patrón seleccionado con
scnoSCN, leído con losResourcesdel propio patrón - los
CharProcsde una fuente Type 3 seleccionada conTf - los image XObjects y las imágenes inline, por el espacio de color que nombra su propio diccionario
- los appearance streams 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 gráfico es DeviceGray (Tabla 52 de ISO 32000-1), así que el relleno es negro DeviceGray, sin calibrar bajo todas las partes salvo que exista un OutputIntent de PDF/A o un DefaultGray. El escaneo rastrea por tanto si algo ha elegido ya 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 ha elegido 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 flujo de contenido, y un escáner tiene que leer ambas con cuidado. El /CS puede ser una abreviatura (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 convierte en 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 flujo 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 del arranque se deduce de este fragmento. /Fm0 nunca se encuentra con un Do, así que su k es irrelevante. El re f pelado de ambas páginas es DeviceGray sin nada que lo calibre. El rg de la página 1 queda cubierto por el DefaultRGB de los recursos con los que pinta la página 1; la página 2 ejecuta el flujo idéntico contra recursos que carecen del mapeo, así que los mismos bytes están sin calibrar 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. El form no aporta nada; invocado, su CMYK añadiría encima la prohibición de mezcla del PDF/A-1
Los espacios de color Default 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 tan locales como los propios recursos; 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 el form que la dibuje, y la misma imagen dibujada desde dos sitios puede quedar calibrada en uno y no en el otro. Los Form XObjects, los patrones de teselado y los procedimientos de glifo Type 3 llevan sus propios Resources, así que el DefaultCMYK de una página no llega dentro de ellos, mientras que un mapeo en su propio diccionario sí. PDFium Component da alcance a los mapeos igual. El veredicto vuelve como un registro TPdfAValidationResult: la Conformance detectada más un conjunto Issues. Es suficiente 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 sin calibrar y no existe ningún OutputIntent de PDF/A. pvaiMissingIccProfile solo significa que existe un intent pero no lleva perfil, cosa que 6.2.2 no acepta como intent de PDF/A. pvaiMixedDeviceColorSpaces cubre tanto la prohibición de mezcla del PDF/A-1 como un desajuste de componentes: DeviceRGB sin calibrar 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 compara qué páginas declaran entradas Default y cuáles no
Cuándo necesita una página transparente su propio espacio de fusión
Una página transparente necesita su propio espacio de color de fusión solo cuando el documento no tiene OutputIntent de PDF/A. ISO 19005-2 6.2.10 (6.2.9 en PDF/A-4) hace del intent el espacio de color de fusión por defecto en el sentido de ISO 32000-1 §11.3.4; sin intent, cada página que contiene transparencia deberá llevar un Group cuyo diccionario de atributos (Tabla 147 de ISO 32000-1) incluya CS. Ese CS también se atiene a 6.2.4, así que un grupo de página /DeviceCMYK bajo un intent sRGB se reporta como si faltara, y PDFium Component lanza pvaiBlendSpaceUndefined para ambos; un espacio de grupo CalRGB o ICCBased no tiene esa dependencia. El 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 fuera; 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 sitios: /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 fusión 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 fusión distinto deNormal; uno declarado pero nunca seleccionado no cuenta - un Form XObject invocado con
Docuyo/Grouptenga/S /Transparency, o cuyo propio contenido cumpla alguna de estas condiciones; un patrón de teselado 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 appearance stream de anotación, incluido cada estado de widget, y un modo de fusión 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, porque un grupo no aislado se compone sobre su fondo, y eso era falso para los grupos de página: la Tabla 147 señala que una página impuesta directamente sobre el medio de salida queda aislada en la práctica 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 solo difieren en /I true. DeviceGray no recibe 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 deja por tanto que un grupo de página calibre diga lo que diga su /I, que un grupo de form o apariencia calibre solo cuando está aislado, mantiene el gris fuera de la calibración por grupos, y sigue comprobando un espacio de dispositivo que un grupo nombre
Un OutputIntent en lugar de un grupo en cada página
Si tu propio código escribe el archivo, empieza con 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 fusión por defecto de cada página transparente. Los grupos de página y las entradas Default tienen el alcance de un diccionario de recursos cada uno, y la página que una fusión posterior añade sin ellos es la que falla. PDF 2.0 añade OutputIntents al objeto de página (ISO 32000-2 §7.7.3.3 y §14.11.5); PDFium Component acepta ese intent de 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, solo miran al catálogo. Para contenido RGB, SaveAsPdfAToStream añade un intent de catálogo construido sobre el perfil ICC sRGB incrustado del escritor PDF/A, y validar la salida muestra si el contenido era de verdad 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 directamente
// IccProfileData sigue 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 significa
// ahora 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