HotXLS schreibt ISO/IEC-29500-Strict-Open-XML-Arbeitsmappen aus Delphi und C++Builder, indem vor dem Speichern eine einzige Eigenschaft gesetzt wird, StrictOOXML. Jeder Teil im Paket, von xl/workbook.xml bis hinunter zu den Beziehungsdateien und Content-Types, wird mit den strengen purl.oclc.org-Vokabularen statt den transitionalen schemas.openxmlformats.org-Vokabularen geschrieben, und Funktionen, die Strict nicht erlaubt, werden beim Speichern mit einer expliziten Exception zurückgewiesen, statt trotzdem geschrieben zu werden
Die meisten Entwickler begegnen dieser Anforderung über ein Beschaffungsdokument. Öffentliche Ausschreibungen in mehreren Rechtsordnungen verlangen die ISO-standardisierte Form von Open XML, nicht die transitionale Form, die Office standardmäßig schreibt, und ein Archiv, das ISO 29500 Strict vorschreibt, weist eine normale .xlsx-Datei zurück, obwohl Excel sie einwandfrei öffnet. Die transitionalen Namensräume existieren, um Legacy-Binärverhalten abzubilden; die strengen sind der eigentliche Standard
Was unterscheidet Strict von Transitional tatsächlich?
Der sichtbare Unterschied ist das Vokabular. Ein strikter Workbook-Teil deklariert http://purl.oclc.org/ooxml/spreadsheetml/main als Wurzel-Namensraum und http://purl.oclc.org/ooxml/officeDocument/relationships für Beziehungsreferenzen, und kein transitionaler Namensraum darf irgendwo im Paket überleben. Beziehungstypen ändern sich entsprechend, sodass der Wurzel-Beziehungsteil .../ooxml/officeDocument/relationships/officeDocument statt des bekannten openxmlformats-Äquivalents benennt, und der Typ für erweiterte Eigenschaften wird als extendedProperties in camelCase geschrieben
Der unsichtbare Unterschied ist der Umfang. Strict lässt bewusst Teile des transitionalen Schemas aus, die nur existierten, um Legacy-Binärdateien verlustfrei hin- und herzukonvertieren, sowie die Herstellererweiterungen, die Office danach hinzufügte. Deshalb ist die Umwandlung kein Suchen-und-Ersetzen über Zeichenketten: Manche Funktionen haben schlicht keine strikte Schreibweise und dürfen gar nicht erst geschrieben werden
Aktivierung
Gewöhnlicher Erstellungscode ändert sich nicht. Die Arbeitsmappe wie gewohnt aufbauen, das Flag setzen und speichern:
var
Wb: TXLSXWorkbook;
Sh: TXLSXWorksheet;
begin
Wb := TXLSXWorkbook.Create;
try
Sh := Wb.Sheets.Add('Data');
Sh.Cells[1, 1].Value := 'Product';
Sh.Cells[1, 2].Value := 'Amount';
Sh.Cells[2, 1].Value := 'Widget';
Sh.Cells[2, 2].Value := 17;
Sh.Cells[3, 2].Formula := '=SUM(B2:B2)';
Wb.StrictOOXML := True; // ISO/IEC 29500 Strict Ausgabe
if Wb.SaveAs('archive-copy.xlsx') <> 1 then
raise Exception.Create('strict save failed');
finally
Wb.Free;
end;
end;
Das Flag wird zu Beginn jeder Speicheroperation zurückgesetzt und aus der Workbook-Eigenschaft neu zugewiesen, sodass eine Exception während eines Speichervorgangs den strikten Modus nicht in den nächsten durchsickern lassen kann. Dieses Detail ist in Serverprozessen wichtig, in denen ein einzelnes Workbook-Objekt mehrere Exportanfragen bedient
Warum kann ein striktes Speichern die Ausführung verweigern?
Vier Funktionsfamilien sind Microsoft-Erweiterungen ohne ISO-29500-Strict-Entsprechung, und HotXLS löst beim Speichern eine Exception aus, statt ein Paket auszugeben, das strikte Konformität behauptet, ohne sie zu erfüllen:
// Strikte Ausgabe kann kein VBA-Projekt einbetten
// -> makrofähige Arbeitsmappen als transitionale .xlsm speichern
// Strikte Ausgabe kann keine Formularsteuerelemente tragen
// -> Schaltflächen, Kontrollkästchen, Kombinationsfelder und ihre ctrlProps
// Strikte Ausgabe kann keine Thread-Kommentare tragen
// -> das moderne Personen-/Thread-Modell, nicht klassische Notizen
// Strikte Ausgabe kann keine Metadaten für dynamische Arrays tragen
// -> Spill-Bereiche, die über den Metadatenteil erfasst werden
Laut zu scheitern ist hier der richtige Kompromiss. Ein still verworfenes VBA-Projekt macht aus einer funktionierenden Arbeitsmappe eine kaputte, die sich trotzdem öffnen lässt, und die Meldung des Fehlers kommt Wochen später von einem Nutzer. Eine Exception benennt die Funktion und die zu ändernde Eigenschaft, während der aufrufende Code noch weiß, was er exportiert hat. Der Erhalt von Makros und externen Verknüpfungen auf dem transitionalen Pfad wird in Erhalt von VBA-Projekten und externen Verknüpfungen behandelt
Zwei Erweiterungsfamilien werden unterschiedlich behandelt, und es lohnt sich zu wissen, warum. Datenbalken, Sparklines und ähnliche Funktionen leben in den Vokabularen x14 und xm, und die SVG-Varianten von Bildern leben in c15. Dies sind Erweiterungslisten-Inhalte, deren Namensräume selbstbeschreibend sind, allgemeine Tabellenkalkulations-Parser tolerieren sie, und es gibt kein ISO-Äquivalent, in das sie übersetzt werden könnten. HotXLS behält sie, statt Nutzerinhalte einfach fallen zu lassen. Wenn ein Validator in der eigenen Pipeline auch bei Erweiterungen streng ist, sollten diese Funktionen vor dem Export aus der Quell-Arbeitsmappe entfernt werden
Die Übersetzung muss Teile erreichen, die normalerweise niemand neu schreibt
Das interessante technische Problem bei der strikten Ausgabe ist nicht die Worksheet-XML. Es sind die Teile, die ein schneller Schreiber lieber wortgetreu kopieren würde. HotXLS erhält Themes, Verbindungen, externe Verknüpfungen, Diagramme und PivotTable-Blobs, indem es deren ursprüngliche komprimierte Bytes direkt durchreicht, was für die Originaltreue genau richtig und für die strikte Ausgabe genau falsch ist, weil kopierte Bytes transitionale Namensräume tragen
Unter StrictOOXML wechseln diese fünf erhaltenen Pfade zum Neuaufbau oder zu einem übersetzenden Replay, wodurch der schnelle Byte-Kopierpfad umgangen wird. Alle XML durchläuft eine einzige Übersetzungsroutine, die sich an doppelt zitierten Attributwerten verankert, sodass eine wie eine URI aussehende Zeichenkette innerhalb einer Zelle niemals versehentlich umgeschrieben werden kann. Zelltext, der dieselbe URI enthält, wird als Entität in der XML maskiert, sodass die verankerte Ersetzung ihn nicht sehen kann. Der Streaming-Writer übersetzt zuerst sein Grundgerüst und teilt dann bei sheetData auf, da die Zeilenblöcke überhaupt keine Vokabular-URIs enthalten. Verwandte Mechanik für den Erhaltungspfad wird in verlustfreie Round-Trips von Themes, Erweiterungslisten und calcChain behandelt
Dateien lesen, die Excel als strikt gespeichert hat
Die Ausgabe ist nur die halbe Geschichte. Excel bietet "Striktes Open-XML-Tabellenblatt" als Speicheroption an, und so erzeugte Dateien müssen korrekt geöffnet werden können. HotXLS normalisiert Beziehungstypen an jeder Stelle, an der Beziehungen im Paket geparst werden – Wurzel, externe Verknüpfungen, Arbeitsblätter, Zeichnungen und PivotTables –, sodass ein strikter Beziehungstyp derselben internen Konstante entspricht wie sein transitionales Gegenstück
Das Gegenstück auf der Leseseite ist die Normalisierung von Namensraumpräfixen, die es erlaubt, beliebige Präfixe und beide Vokabulare auf eine einzige kanonische Namenstabelle abzubilden. Diese Arbeit kommt gewöhnlichen Dateien ebenso zugute wie strikten, da Drittanbieter-Generatoren Präfixe frei vergeben, und es ist dieselbe Mechanik, die in OPC-Beziehungsauflösung in XLSX-Paketen beschrieben wird
Eine kurze Checkliste vor dem Versand strikter Ausgabe
Mit dem Paket prüfen, nicht mit Excel. Excel öffnet beide Formen anstandslos, daher beweist ein erfolgreiches Öffnen nichts über die Konformität. Das Ergebnis entpacken und prüfen, dass xl/workbook.xml den purl-Namensraum deklariert, dass kein Teil schemas.openxmlformats.org/spreadsheetml enthält und dass Beziehungstypen in _rels/.rels und xl/_rels/workbook.xml.rels die strikten Formen verwenden
Dann die Datei über HotXLS erneut öffnen und Werte, Formeln, Formate und Hyperlinks mit der Quelle vergleichen. Ein Rücklesetest ist der einzige günstige Weg zu beweisen, dass die Übersetzung den Inhalt nicht beschädigt hat, und er beansprucht gleichzeitig die Normalisierung auf der Leseseite. Wenn die eigenen Arbeitsmappen Diagramme tragen, sollten auch diese geprüft werden, da der Diagrammteil einer der erhaltenen Teile ist, der im strikten Modus auf einen neu aufgebauten Pfad umschaltet
Strikte Ausgabe, tolerantes Lesen und verlustfreier Erhalt sind alle Teil derselben OOXML-Engine für Delphi und C++Builder; die vollständige Funktionsliste findet sich auf der HotXLS-Delphi-Tabellenkalkulationskomponentenseite