Technický článek

Zápis souborů XLSX ve formátu ISO 29500 Strict z Delphi

HotXLS zapisuje sešity Open XML ve formátu ISO/IEC 29500 Strict z Delphi a C++Builder nastavením jediné vlastnosti, StrictOOXML, před uložením. Každá část balíčku, od xl/workbook.xml až po soubory vztahů a typy obsahu, se zapisuje se striktními slovníky purl.oclc.org namísto přechodných slovníků schemas.openxmlformats.org, a funkce, které Strict nepovoluje, jsou odmítnuty výslovnou výjimkou, místo aby se přesto zapsaly

Většina vývojářů se s tímto požadavkem setká přes zadávací dokumentaci. Veřejné zakázky v několika jurisdikcích požadují ISO standardizovanou formu Open XML, ne přechodnou formu, kterou Office ve výchozím stavu zapisuje, a archiv, který vyžaduje ISO 29500 Strict, odmítne běžný soubor .xlsx, i když jej Excel otevře bez problémů. Přechodné jmenné prostory existují kvůli zachování chování starších binárních formátů; striktní jsou samotný standard

Co se ve skutečnosti liší mezi Strict a Transitional?

Viditelný rozdíl je ve slovníku. Striktní část sešitu deklaruje http://purl.oclc.org/ooxml/spreadsheetml/main jako svůj kořenový jmenný prostor a http://purl.oclc.org/ooxml/officeDocument/relationships pro odkazy na vztahy, a nikde v balíčku nesmí přežít žádný přechodný jmenný prostor. S tím se mění i typy vztahů, takže kořenová část vztahů pojmenovává .../ooxml/officeDocument/relationships/officeDocument namísto známého ekvivalentu openxmlformats, a typ rozšířených vlastností je zapsán stylem camelCase jako extendedProperties

Neviditelný rozdíl je v rozsahu. Strict záměrně vynechává části přechodného schématu, které existovaly jen kvůli zpětné kompatibilitě se staršími binárními soubory, spolu s rozšířeními dodanými Office později. Proto převod není hledáním a nahrazováním v řetězcích: některé funkce prostě nemají striktní zápis a nesmí se vůbec zapisovat

Zapnutí funkce

Běžný kód pro tvorbu obsahu se nemění. Sestavte sešit obvyklým způsobem, nastavte příznak a uložte:

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;          // výstup ISO/IEC 29500 Strict
    if Wb.SaveAs('archive-copy.xlsx') <> 1 then
      raise Exception.Create('strict save failed');
  finally
    Wb.Free;
  end;
end;

Příznak se resetuje na začátku každé operace uložení a znovu se přiřadí z vlastnosti sešitu, takže výjimka během jednoho uložení nemůže striktní režim prosáknout do dalšího. Tento detail má význam v serverových procesech, kde jeden objekt sešitu obsluhuje více exportních požadavků

Proč může striktní uložení odmítnout proběhnout?

Čtyři skupiny funkcí jsou rozšíření Microsoftu bez ekvivalentu v ISO 29500 Strict, a HotXLS při ukládání vyvolá výjimku, místo aby vydal balíček, který tvrdí, že je striktně konformní, a přitom není:

// Striktní výstup nemůže vložit projekt VBA
//   -> sešity s makry uložte jako přechodné .xlsm
// Striktní výstup nemůže nést ovládací prvky formuláře
//   -> tlačítka, zaškrtávací pole, rozevírací seznamy a jejich ctrlProps
// Striktní výstup nemůže nést vláknové komentáře
//   -> moderní model persons/threads, ne klasické poznámky
// Striktní výstup nemůže nést metadata dynamických polí
//   -> rozlévané oblasti zaznamenané v části metadat

Hlasité selhání je zde správný kompromis. Tiše zahozený projekt VBA promění funkční sešit v rozbitý, který se přesto otevře, a zpráva o selhání dorazí od uživatele o týdny později. Výjimka pojmenuje funkci i vlastnost, kterou je třeba změnit, zatímco volající kód ještě ví, co exportoval. Zachování maker a externích odkazů na přechodné cestě popisuje článek zachování projektů VBA a externích odkazů

Dvě skupiny rozšíření se řeší jinak, a stojí za to vědět proč. Datové pruhy, minigrafy a podobné funkce žijí ve slovnících x14 a xm, a varianty obrázků SVG žijí v c15. Jde o obsah rozšiřujícího seznamu, jehož jmenné prostory jsou samopopisné, obecné parsery tabulek je tolerují a neexistuje žádný ekvivalent ISO, do kterého by se daly převést. HotXLS je ponechává, místo aby zahodil uživatelský obsah. Pokud je validátor ve vaší pipeline striktní i ohledně rozšíření, ne jen jmenných prostorů, odstraňte tyto funkce ze zdrojového sešitu ještě před exportem

Převod musí zasáhnout části, které běžně nikdo nepřepisuje

Zajímavý inženýrský problém striktního výstupu není XML listu. Je to části, které by rychlý zapisovač raději zkopíroval doslovně. HotXLS zachovává motivy, připojení, externí odkazy, grafy a bloky kontingenčních tabulek tím, že kopíruje jejich původní komprimované bajty přímo skrz, což je přesně to správné pro věrnost a přesně to špatné pro striktní výstup, protože zkopírované bajty nesou přechodné jmenné prostory

Pod StrictOOXML těchto pět zachovávaných cest přepne na přestavbu nebo na překládané přehrání, čímž obejde rychlou cestu kopírování bajtů. Veškeré XML prochází jedinou překladovou rutinou, která se ukotvuje na hodnotách atributů v uvozovkách, takže řetězec vypadající jako URI uvnitř buňky nemůže být nikdy omylem přepsán. Text buňky obsahující stejné URI je v XML escapován jako entita, takže jej ukotvené nahrazení nevidí. Streamovací zapisovač nejprve přeloží svou kostru a poté se rozdělí u sheetData, protože bloky řádků neobsahují vůbec žádná URI slovníků. Související mechaniku pro cestu zachování popisuje článek bezztrátové obousměrné převody motivů, seznamů rozšíření a calcChain

Čtení souborů, které Excel uložil jako strict

Výstup je jen polovina příběhu. Excel nabízí jako možnost uložení "Strict Open XML Spreadsheet" a soubory takto vytvořené se musí otevřít správně. HotXLS normalizuje typy vztahů na každém místě, kde se v balíčku vztahy parsují, u kořene, externích odkazů, listů, kreseb a kontingenčních tabulek, takže striktní typ vztahu odpovídá stejné interní konstantě jako jeho přechodný protějšek

Protějšek na straně čtečky je normalizace prefixů jmenných prostorů, která umožňuje libovolné prefixy a oba slovníky rozřešit do jedné kanonické tabulky názvů. Tato práce prospívá běžným souborům stejně jako striktním, protože generátory třetích stran vážou prefixy volně, a jde o stejnou mechaniku popsanou v článku rozřešení vztahů OPC v balíčcích XLSX

Krátký kontrolní seznam před vydáním striktního výstupu

Ověřujte balíček, ne Excel. Excel ochotně otevře obě formy, takže úspěšné otevření o shodě nic nedokazuje. Rozbalte výsledek a potvrďte, že xl/workbook.xml deklaruje jmenný prostor purl, že žádná část neobsahuje schemas.openxmlformats.org/spreadsheetml a že typy vztahů v _rels/.rels a xl/_rels/workbook.xml.rels používají striktní formy

Poté soubor znovu otevřete přes HotXLS a porovnejte hodnoty, vzorce, formáty a hypertextové odkazy se zdrojem. Test zpětného čtení je jediný levný způsob, jak dokázat, že převod obsah nepoškodil, a zároveň prověřuje normalizaci na straně čtečky. Pokud vaše sešity obsahují grafy, zkontrolujte i ty, protože část grafu je jednou ze zachovávaných částí, která ve striktním režimu přepíná na přestavěnou cestu

Striktní výstup, tolerantní čtení a bezztrátové zachování jsou součástí stejného motoru OOXML pro Delphi a C++Builder; kompletní seznam funkcí je na stránce komponenty HotXLS pro tabulky v Delphi