Cuando HotXLS exporta una hoja de cálculo a PDF con el etiquetado automático activado, las imágenes de la hoja que llevan texto alternativo se emiten ahora como elementos de estructura /Figure independientes con una entrada Unicode /Alt, identificadores de contenido marcado densos y locales de página, y entradas exactas en el parent tree. Las imágenes sin texto alternativo siguen siendo artifacts decorativos, y los gráficos siguen siendo artifacts también. Ese alcance preciso importa: hace que las imágenes informativas sean alcanzables para un lector de pantalla, y no es lo mismo que la conformidad completa con PDF/UA
La mecánica que hay detrás es más interesante que la descripción de la función, porque dos de sus piezas son del tipo de detalle que produce en silencio un PDF estructuralmente válido cuya estructura apunta al contenido equivocado
Qué cuenta como imagen informativa?
Solo un AltText no vacío. La propiedad TXLSXImage.AltText hace ida y vuelta con el atributo OOXML descr de las propiedades no visuales de la imagen, que es donde Excel guarda el texto que un usuario teclea en el panel de texto alternativo. Es la única señal del fichero de que el autor consideró que la imagen lleva información y no decoración, así que es la única señal en la que confía el exportador
Dos cuasi casos no se aceptan deliberadamente. El campo de título, guardado por separado de la descripción, no es un sustituto: un título es un nombre para el objeto, no un equivalente textual del mismo, y promoverlo a /Alt produciría un documento que supera una comprobación automatizada mientras anuncia «Imagen 3» a un lector de pantalla. Una descripción vacía tampoco es un hueco que rellenar con un placeholder; significa que la imagen sigue siendo un artifact, que es el desenlace correcto para un logo o un filete separador. Los gráficos siguen siendo artifacts por ahora, porque el equivalente textual de un gráfico son sus datos y sintetizar uno desde las series sería inventar y no extraer
uses
lxHandleX, lxPDF;
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Exporter: TXLSPDFExport;
I: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('regional-review.xlsx');
Sheet := Book.Sheets.ByPos[0];
// Auditar antes de exportar: una imagen sin descripción
// se exportará como artifact decorativo
for I := 0 to Sheet.Images.Count - 1 do
if Sheet.Images[I].AltText = '' then
Sheet.Images[I].AltText := DescribeImage(Sheet.Images[I].Name);
Exporter := TXLSPDFExport.Create;
try
Exporter.TagMode := xlsPdfTagsAutomatic;
Exporter.DocumentLanguage := 'en-US';
Exporter.SaveAsPDF(Sheet, 'regional-review.pdf');
finally
Exporter.Free;
end;
finally
Book.Free;
end;
end;
Por qué la página necesita un único alocador de MCID?
Porque el parent tree es un array indexado por identificador de contenido marcado, y dos alocadores producen dos entradas que reclaman la misma ranura. El PDF etiquetado conecta contenido y estructura en ambas direcciones. En el lado del contenido, un tramo del content stream de la página se envuelve en operadores BDC y EMC que llevan un número /MCID único dentro de esa página. En el lado de la estructura, el diccionario de página lleva una clave /StructParents que nombra una fila del /ParentTree del documento, y esa fila es un array cuyo elemento en el índice n es el elemento de estructura dueño del MCID n
Una página de hoja de cálculo contiene celdas de tabla y, ahora, figuras. Si el etiquetador de celdas cuenta sus identificadores desde cero y el etiquetador de figuras también cuenta desde cero, la primera figura reclama la ranura que la primera celda ya posee. Nada del fichero resultante está lo bastante mal formado para que un parser lo rechace: el árbol de estructura está intacto, el contenido marcado está equilibrado y un validador ve un documento con parent tree. Lo que recibe un lector de pantalla es una celda de tabla anunciada como imagen, o una imagen anunciada con el texto de una celda. El exportador aloca, por tanto, desde un contador a nivel de página compartido por ambos etiquetadores, y congela el record de página solo cuando se conoce el número de objeto de la página, ya que la fila del parent tree no puede escribirse antes de que la página a la que se refiera tenga identidad
La Figure debe envolver toda la instancia visible
La colocación ingenua es envolver el operador Do que invoca el XObject de imagen, ya que es el operador que dibuja la imagen. No basta. Una imagen de hoja de cálculo suele dibujarse con una sombra detrás y una ruta de recorte alrededor, y esas marcas son parte del objeto visible. Dejadas fuera del ámbito /Figure se convierten en contenido sin marcar, que es exactamente el estado que marca una auditoría de estructura
Así que el ámbito de contenido marcado se abre antes de la sombra y se cierra después del dibujo de la imagen, cubriendo también el recorte. El uso compartido se conserva donde el uso compartido es correcto: dos celdas que muestran la misma carga de imagen siguen referenciando un XObject de imagen, porque eso es una optimización a nivel de recurso y no tiene nada que ver con la semántica. Lo que recibe cada instancia visible es su propio MCID y su propio elemento de estructura, porque dos apariciones del mismo logo en sitios distintos son dos cosas que un lector encuentra. La colocación de imágenes y la geometría EMU que posiciona estos objetos se cubren en el artículo de geometría de imágenes
Orden de lectura en una página de hoja de cálculo
El orden de lectura es una decisión que el exportador tiene que tomar, porque una hoja de cálculo no tiene un flujo redactado como lo tiene un documento. La regla adoptada es estable y fácil de explicar: para cada página, primero la tabla, después las figuras en orden de dibujo. Un lector oye así el contenido tabular de la página y luego sus imágenes, en lugar de tener imágenes intercaladas en la posición que los objetos de dibujo resultaran ocupar en el fichero
Ese orden es por página y no por documento, lo que importa en un libro que pagina en decenas de páginas: la rama de estructura de cada página es autocontenida, así que un lector que se mueve entre páginas no salta de vuelta a una tabla anterior. Si necesitáis controlar cómo pagina la hoja en primer lugar, la interacción de configuración de página y área de impresión se describe en el artículo de protección y configuración de página
Qué certifica esto y qué no
Certifica que las imágenes informativas llegan a la tecnología de asistencia con la descripción aportada por su autor, y que el mapeo de contenido a estructura es correcto y no solo presente. No hace que la salida sea conforme a PDF/UA, y describirla así sería una afirmación que la implementación no puede sostener: los gráficos siguen siendo artifacts, y una declaración completa de conformidad exige una auditoría de cada tipo de estructura, cada fuente y los metadatos del documento en conjunto
Si vuestro requisito es un perfil de archivo o de conformidad más que una mejora de accesibilidad, esa es otra configuración de exportación y otro conjunto de comprobaciones, descrito en el artículo de exportación de archivo PDF/A. Ambas se combinan, pero responden a auditores distintos
Una sugerencia práctica para un pipeline de informes: auditad el texto alternativo en el punto donde se genera el libro, no en el momento de exportar. El generador sabe qué representa cada imagen de gráfico o diagrama incrustado, y puede escribir una descripción real en AltText; una pasada en el momento de exportar solo puede deciros que falta una descripción. HotXLS lee y escribe XLS, XLSX, ODS y CSV de forma nativa desde Delphi y C++Builder sin dependencia de Excel, y sus opciones de configuración de exportación están en la página de producto de HotXLS Delphi spreadsheet component