Wenn HotXLS ein Arbeitsblatt mit aktiviertem automatischen Tagging nach PDF exportiert, emittieren Arbeitsblatt-Bilder, die Alternativtext tragen, nun eigenständige /Figure-Strukturelemente mit einem Unicode-/Alt-Eintrag, dichten seitenlokalen Marked-Content-Kennungen und exakten Parent-Tree-Einträgen. Bilder ohne Alternativtext bleiben dekorative Artifacts, und Diagramme bleiben ebenfalls Artifacts. Dieser präzise Umfang wiegt schwer: Er macht informative Bilder für einen Screenreader erreichbar, und es ist nicht dasselbe wie volle PDF/UA-Konformität
Die Mechanik dahinter ist interessanter als die Feature-Beschreibung, denn zwei davon sind die Art von Detail, die still ein strukturell gültiges PDF produziert, dessen Struktur auf den falschen Inhalt zeigt
Was gilt als informatives Bild?
Nur ein nicht leerer AltText. Die Property TXLSXImage.AltText führt das OOXML-descr-Attribut der Non-Visual-Properties des Bildes im Round-Trip mit, und genau dort speichert Excel den Text, den ein Nutzer in das Alt-Text-Feld tippt. Das ist das einzige Signal in der Datei, dass der Autor das Bild als informationstragend statt dekorativ einschätzte, also ist es das einzige Signal, dem der Exporteur vertraut
Zwei Beinahe-Treffer werden bewusst nicht akzeptiert. Das Titelfeld, getrennt von der Beschreibung gespeichert, ist kein Ersatz: Ein Titel ist ein Name für das Objekt, nicht ein textliches Äquivalent von ihm, und ihn in /Alt zu befördern würde ein Dokument produzieren, das eine automatisierte Prüfung besteht, während es einem Screenreader „Bild 3“ ansagt. Eine leere Beschreibung ist auch keine Lücke, die ein Platzhalter füllen könnte; sie bedeutet, dass das Bild ein Artifact bleibt, was das korrekte Ergebnis für ein Logo oder eine Trennlinie ist. Diagramme bleiben vorerst ebenfalls Artifacts, denn das textliche Äquivalent eines Diagramms sind seine Daten, und eines aus den Reihen zu synthetisieren wäre Erfindung statt Extraktion
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];
// Vor dem Export prüfen: Ein Bild ohne Beschreibung
// wird als dekoratives Artifact exportiert
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;
Warum braucht die Seite einen einzigen MCID-Zuteiler?
Weil der Parent Tree ein Array ist, das über die Marked-Content-Kennung indiziert wird, und zwei Zuteiler zwei Einträge produzieren, die denselben Platz beanspruchen. Getaggtes PDF verbindet Inhalt und Struktur in beide Richtungen. Auf der Inhaltsseite ist eine Spanne des Seiten-Content-Streams in BDC- und EMC-Operatoren verpackt, die eine /MCID-Nummer tragen, die innerhalb dieser Seite eindeutig ist. Auf der Strukturseite trägt das Seiten-Dictionary einen /StructParents-Schlüssel, der eine Zeile des Dokument-/ParentTree benennt, und diese Zeile ist ein Array, dessen Element am Index n das Strukturelement ist, das MCID n besitzt
Eine Arbeitsblattseite enthält Tabellenzellen und nun auch Figuren. Zählt der Zell-Tagger seine Kennungen ab null und der Figure-Tagger ebenfalls ab null, beansprucht die erste Figur den Platz, den die erste Zelle bereits besitzt. Nichts an der entstehenden Datei ist fehlerhaft genug, als dass ein Parser sie zurückweist: Der Strukturbaum ist intakt, der Marked Content ist ausgeglichen, und ein Validator sieht ein Dokument mit einem Parent Tree. Was ein Screenreader bekommt, ist eine Tabellenzelle, die als Bild angesagt wird, oder ein Bild, das mit dem Text einer Zelle angesagt wird. Der Exporteur teilt deshalb von einem einzigen Zähler auf Seitenebene zu, den beide Tagger gemeinsam nutzen, und friert den Seiten-Datensatz erst ein, wenn die Seitenobjektnummer bekannt ist, denn die Parent-Tree-Zeile lässt sich nicht schreiben, bevor die Seite, auf die sie sich bezieht, eine Identität hat
Die Figur muss die gesamte sichtbare Instanz umschließen
Die naive Platzierung ist, den Do-Operator zu umschließen, der das Bild-XObject aufruft, denn das ist der Operator, der das Bild zeichnet. Das reicht nicht. Ein Arbeitsblatt-Bild wird oft mit einem Schatten dahinter und einem Beschneidungspfad darum gezeichnet, und diese Markierungen sind Teil des sichtbaren Objekts. Außerhalb des /Figure-Geltungsbereichs gelassen werden sie zu unmarkiertem Inhalt, was exakt der Zustand ist, den eine Strukturprüfung anmarkt
Der Marked-Content-Geltungsbereich öffnet sich also vor dem Schatten und schließt nach dem Bildzeichnen, und umfasst auch den Clip. Teilen bleibt bewahrt, wo Teilen korrekt ist: Zwei Zellen, die dieselbe Bild-Nutzlast zeigen, referenzieren weiterhin ein einziges Bild-XObject, denn das ist eine Optimierung auf Ressourcenebene und hat nichts mit Semantik zu tun. Was jede sichtbare Instanz bekommt, ist ihre eigene MCID und ihr eigenes Strukturelement, denn zwei Vorkommen desselben Logos an verschiedenen Orten sind zwei Dinge, die ein Leser antrifft. Bildplatzierung und die EMU-Geometrie, die diese Objekte positioniert, behandelt der Artikel zur Bildgeometrie
Lesereihenfolge auf einer Arbeitsblattseite
Die Lesereihenfolge ist eine Entscheidung, die der Exporteur treffen muss, denn eine Tabelle hat keinen verfassten Fluss wie ein Dokument. Die gewählte Regel ist stabil und leicht zu erklären: Je Seite kommt zuerst die Tabelle, dann die Figuren in Zeichnenreihenfolge. Ein Leser hört also den tabellarischen Inhalt der Seite und dann ihre Bilder, statt Bilder verschränkt an der Position zu bekommen, die die Zeichnungsobjekte zufällig in der Datei einnahmen
Diese Reihenfolge gilt je Seite statt je Dokument, was bei einer Arbeitsmappe zählt, die in Dutzende Seiten paginiert: Der Strukturzweig jeder Seite ist in sich geschlossen, also springt ein Leser, der zwischen Seiten wechselt, nicht in eine frühere Tabelle zurück. Wer Kontrolle darüber braucht, wie das Blatt überhaupt paginiert, dem ist die Interaktion von Seiteneinrichtung und Druckbereich beschrieben in dem Artikel zu Schutz und Seiteneinrichtung
Was das bescheinigt und was nicht
Es bescheinigt, dass informative Bilder die Hilfstechnologie mit ihrer vom Autor gelieferten Beschreibung erreichen, und dass die Inhalt-zu-Struktur-Zuordnung korrekt ist statt bloß vorhanden. Es macht die Ausgabe nicht PDF/UA-konform, und es so zu beschreiben wäre eine Behauptung, die die Implementierung nicht stützen kann: Diagramme sind weiterhin Artifacts, und eine vollständige Konformitätserklärung verlangt eine Prüfung jedes Strukturtyps, jeder Schrift und der Dokumentmetadaten als Ganzem
Ist Ihre Anforderung ein Archiv- oder Konformitätsprofil statt einer Verbesserung der Barrierefreiheit, ist das eine andere Exportkonfiguration und ein anderer Satz Prüfungen, beschrieben in dem Artikel zum PDF/A-Archivexport. Beides kombiniert sich, aber sie antworten verschiedenen Prüfern
Ein praktischer Vorschlag für eine Berichtspipeline: Alternativtext dort prüfen, wo die Arbeitsmappe erzeugt wird, nicht zur Exportzeit. Der Generator weiß, was jedes Diagrammbild oder eingebettete Schaubild darstellt, und kann eine echte Beschreibung in AltText schreiben; ein Durchlauf zur Exportzeit kann nur sagen, dass eine Beschreibung fehlt. HotXLS liest und schreibt XLS, XLSX, ODS und CSV nativ aus Delphi und C++Builder ohne Excel-Abhängigkeit, und seine Exportkonfigurationsoptionen listet die Produktseite der HotXLS Delphi Tabellenkomponente