Odborný článok

Vkladanie objektov grafov na hárky Excelu s HotXLS

HotXLS dokáže umiestniť graf priamo na pracovný hárok, ukotvený na rozsah buniek, namiesto toho, aby ho dal na osobitný hárok grafu. V BIFF8 terminológii to znamená zapísať kresliaci tvar s OBJ záznamom typu 5 a zaparkovať substream grafu na konci prúdu záznamov hárku, čo je presne rozloženie, ktoré Excel produkuje, a presne miesto, kde ho čítačka očakáva

Rozlišovanie záleží každému, kto generuje prevádzkové reporty. Hárok grafu je slušný domov pre jediný hlavný vizuál. Mesačný regionálny rozpad chce graf vedľa čísel, ktoré zhrňuje, na tom istom hárku, dimenzovaný na blok buniek, ku ktorému patrí, takže čitateľ scrolluje raz namiesto prepínania kariet a straty kontextu

Čítanie už bolo, zápis nie

Asymetriu stojí za pomenovanie, lebo formuje prácu. HotXLS už vedel čítať vložené grafy: keď prúd záznamov pracovného hárku obsahuje BOF označený ako substream grafu, parser prepnie kontext, nazbiera záznamy grafu a pri uzatvárajúcom EOF ich odovzdá späť kresliacemu tvaru, ktorý predstavil OBJ záznam. Touto cestou prešiel každý zošit od Excelu, ktorý knižnica kedy otvorila

Chýbala autorská strana a užitočným dôsledkom je, že nový writer mal presnú špecifikáciu, ktorú zasiahnuť: vyprodukovať bajtové rozloženie, ktoré existujúca čítačka už teraz znovu pripája. Lepšie akceptačné kritérium pre funkciu binárneho formátu než nezávisle napísaná čítačka, ktorú ste nemohli meniť, neexistuje

Z čoho sa vložený graf skladá

Tri kusy sa musia zhodnúť. Kresliaca vrstva prispieva tvarom hostiteľského ovládacieho prvku, objektová vrstva prispieva OBJ záznamom, ktorej common object data deklaruje typ objektu 5, a prúd záznamov prispieva samotným substreamom grafu. Príznaky volieb na OBJ zázname sú tie, ktoré Excel píše pre rámec grafu: positioned, locked, automatic line a automatic fill, a práve to robí vložený graf správajúcim sa ako natívny, keď naň používateľ klikne

HotXLS ukotví BIFF8 substream grafu k pracovnému hárku v Delphi cez tri sa zhodujúce kusy: tvar hostiteľského ovládacieho prvku kresliacej vrstvy, OBJ záznam, ktorej common object data deklaruje typ objektu 5, a reťaz záznamov grafu zaparkovaný na konci prúdu záznamov hárku, kde BOF grafu prepne kontext parsera a uzatvárajúci EOF znovu pripojí záznamy
Tri vrstvy nesú jeden vložený graf: kresliaci tvar ho ukotví, OBJ záznam ho typuje ako hostiteľa grafu a substream grafu na konci prúdu hárku dodá záznamy, ktoré čítačka znovu pripojí

Ukotvenie si zaslúži poznámku, pretože je bežným zdrojom off-by-one chýb. API HotXLS berie čísla riadkov a stĺpcov od jednej, v súlade so zvyškom knižnice, a client anchor zapisovaný do súboru je od nuly. Konverzia sa deje vo vnútri AddChartObject, takže volajúci zostávajú v súradnicovom systéme, ktorý používajú všade inde, ale každý, kto porovnáva hex dump s vlastným volaním, si musí pamätať, na ktorej strane tej hranice číta

var
  Book: TXLSWorkbook;
  Sheet: TXLSWorksheet;
  Series: array[0..1] of TXLSChartSeriesInfo;
begin
  Book := TXLSWorkbook.Create(nil);
  try
    Book.LoadFromFile('regional-sales.xls');
    Sheet := Book.Sheets[0];

    FillChar(Series, SizeOf(Series), 0);
    Series[0].Name := 'Actual';
    Series[0].Categories := 'Data!$A$2:$A$13';
    Series[0].Values := 'Data!$B$2:$B$13';
    Series[0].DataLabels.ShowValue := True;
    Series[0].HasDataLabels := True;

    Series[1].Name := 'Target';
    Series[1].Categories := 'Data!$A$2:$A$13';
    Series[1].Values := 'Data!$C$2:$C$13';
    Series[1].SecondaryAxis := True;

    // Ukotvené na E2:M20 na tomto hárku, od jednej
    Sheet.AddChartObject(xlsChartTypeColumn, 'Regional sales',
      'Month', 'Amount', Series, 2, 5, 20, 13);

    Book.SaveToFile('regional-sales-charted.xls');
  finally
    Book.Free;
  end;
end;

FillChar na poli sérií nie je dekorácia. TXLSChartSeriesInfo nesie niekoľko voliteľných podzáznamov, dátové menovky, štýl po sérii, trendové línie a chybové úseky, každý strážený boolovskou hodnotou, a čiastočne inicializovaný záznam na zásobníku podá emitéru príznaky, ktoré nikto nenastavil. Vynulujte pole a potom nastavte polia, ktoré máte na mysli

Ktoré referencie sérií akceptuje vložená cesta?

Obyčajné rozsahy v štýle A1 vo vnútri toho istého zošitu a toto obmedzenie je zámerné, nie opomenutie. Každá referencia sa resolveuje voči zoznamu hárkov zošitu a mení sa na index externej referencie, ktorý záznamy grafu potrebujú. Pomenovaný rozsah alebo referencia na externý zošit padne na zástupný symbol s parsovaným výrazom nulovej dĺžky, takže graf sa zapíše čisto, ale tá konkrétna séria nemá zdroj dát, dokiaľ ju nepointujete na rozsah

Akceptovanie referencií sérií HotXLS na vloženej BIFF8 ceste grafu: rozsahy v štýle A1 ako Data!$B$2:$B$13 vo vnútri toho istého zošitu sa resolveujú voči zoznamu hárkov na index externej referencie, ktorý záznamy grafu potrebujú, kým pomenované rozsahy a referencie na externé zošity padnú na zástupný symbol s parsovaným výrazom nulovej dĺžky, pričom oboje kryje AddChartSheet
Len obyčajné rozsahy v štýle A1 vo vnútri toho istého zošitu sa skompilujú do referencií sérií grafu; všetko ostatné sa zapíše čisto ako zástupný symbol, dokiaľ sa nepresmeruje, a plná cesta býva na AddChartSheet

Dôvodom je priamy inžiniersky kompromis. Plná cesta kompilácie referencií existuje na trase hárku grafu, zabalená vo vrstve kolekcie pracovných hárkov, a jej čisté vyzdvihnutie by znamenalo duplikovať sto riadkov resolveovacej logiky pre prípad, ktorý je v praxi neobvyklý. Vložený graf takmer vždy vykresľuje bunky na vlastnom hárku alebo na sesterskom dátovom hárku. Pomenované a externé referencie sú kryté na trase hárku grafu cez AddChartSheet, takže nič nie je nedostupné, len sa to dosiahne z iného vstupného bodu

Všetko ostatné v modeli sérií funguje identicky na oboch trasách. Viazanie sekundárnej osi, štýl čiary, výplne a markera po sérii, trendové línie, chybové úseky a dátové menovky sú všetky súčasťou TXLSChartSeriesInfo a všetky sa emitujú rovnako, takže definícia grafu sa môže presunúť medzi vloženým objektom a hárkom grafu so zmenou len volania. Mechanika skupín osí za príznakom sekundárnej osi je rozobraná v článku skupiny sekundárnej osi pri zápise BIFF

Prečo sa názov grafu prečítal ako dva znaky?

Pretože sa podal počet znakov tam, kde sa očakával počet bajtov, a BIFF Unicode reťazce túto chybu uľahčujú napísať a sťažujú vidieť. Krátky BIFF Unicode reťazec začína počtom znakov a bajtom príznakov a bajt príznakov nesie bit high-byte, ktorý hovorí, či payload má jeden bajt na znak alebo dva. Prečítajte 16-bitový payload s počtom znakov, ako keby to bola dĺžka v bajtoch, a dostanete presne polovicu reťazca: séria pomenovaná Sales sa vráti ako Sa a názov grafu sa skráti rovnako, pretože názvy a menovky sérií zdieľajú dekódovaciu cestu

To, čo robí tento defekt pozoruhodným, je, že sa trikrát zopakoval v tej istej rodine záznamov, raz v menách trendových línií, raz v menách pivot grafov a raz v názvoch grafov. Každý výskyt vyzeral ako čerstvá chyba v novej funkcii. Všetky tri boli to isté chýbajúce násobenie. Pravidlo, ktoré to napokon uzavrelo, je mechanické a malo by sa aplikovať bez úsudku: vždy, keď čítate jeden z týchto reťazcov, pozrite si najprv príznak high-byte a vynásobte počet znakov šírkou payloadu pred tým, než siahnete na buffer. Detaily na úrovni záznamov sú v článku dekódovanie počtov znakov XLUnicodeString a príznaku high-byte

// Vložený graf zdieľa kresliacu vrstvu s obrázkami a tvarmi, takže
// existujúca kresba na hárku sa zachová. AddChartObject vracia index
// vytvoreného objektu
var
  ObjIndex: Integer;
begin
  ObjIndex := Sheet.AddChartObject(xlsChartTypeLine, 'Trend',
    'Week', 'Units', Series, 2, 8, 18, 16);
  if ObjIndex < 0 then
    raise Exception.Create('chart object was not created');
end;

Kam sa vložené grafy hodia oproti alternatívam

Existujú tri trasy a odpovedajú na odlišné otázky. Vložený objekt grafu patrí vedľa svojich dát na pracovný hárok a to je to, čo chcú väčšina reportov. Hárok grafu sa hodí k jedinému prezentačnému vizuálu a dá vám plnú cestu kompilácie referencií. Zachovanie existujúceho grafu z načítaného súboru, nedotknutého, je správna odpoveď, keď zošit prišiel z Excelu s formátovaním, ktoré nechce nikto, aby knižnica reinterpretovala; toto prechodové správanie popisuje článok zachované ChartML a kombinované grafy

Keďže vložený graf jazdí na kresliacej vrstve, koexistuje s obrázkami a tvarmi na tom istom hárku namiesto toho, aby ich nahrádzal, a všeobecný model tej vrstvy je rozobraný v článku grafy, obrázky a kresby v HotXLS. Všetky tri trasy prichádzajú v HotXLS Delphi tabuľkovom komponente, takže voľba je o tom, ako má report vyzerať, nie o tom, čo knižnica dokáže vyjadriť

Metodologický bod je ten, ktorý stojí za ponechanie. Keď má funkcia binárneho formátu existujúcu čítačku, stavte writer proti čítačke, nie proti svojmu čítaniu špecifikácie. Čítačka kóduje roky kontaktu so súbormi, ktoré reálne aplikácie skutočne vyprodukovali, vrátane častí, ktoré špecifikácia popisuje voľne, a writer, ktorý ju uspokojí, je oveľa pravdepodobnejšie, že uspokojí aj Excel