Tehnički članak

Ugrađivanje objekata grafikona na radne listove uz HotXLS

HotXLS može da postavi grafikon direktno na radni list, sidren na opseg ćelija, umesto da ga stavi na poseban chart sheet. U BIFF8 terminima to znači upisati drawing oblik sa OBJ zapisom tipa 5 i parkirati chart substream na kraj toka zapisa lista, što je baš raspored koji Excel proizvodi i baš mesto gde čitač očekuje da ga nađe

Razlika je važna svakome ko generiše operativne izveštaje. Chart sheet je fina kuća za jedan naslovni vizuelni prikaz. Mesečni regionalni pregled želi grafikon pored brojeva koje sumira, na istom listu, dimenzionisan na blok ćelija kojem pripada, da čitalac skroluje jednom umesto da menja tabove i gubi kontekst

Čitanje je već postojalo, pisanje nije

Asimetriju vredi imenovati jer oblikuje posao. HotXLS je već umeo da čita ugrađene grafikone: kad tok zapisa radnog lista sadrži BOF označen kao chart substream, parser menja kontekst, sakuplja zapise grafikona, i pri zatvarajućem EOF vraća ih drawing obliku koji je OBJ zapis uveo. Tom putanjom je prošla svaka radna sveska autorstva Excel-a koju je biblioteka ikada otvorila

Nedostajala je autorska strana, i korisna posledica je da je novi pisac imao preciznu specifikaciju koju treba pogoditi: proizvedi bajt raspored koji postojeći čitač već ponovo kači. Ne postoji bolji kriterijum prihvatanja za mogućnost binarnog formata od nezavisno napisanog čitača koji niste smeli menjati

Od čega se sastoji ugrađeni grafikon

Tri dela moraju da se slažu. Drawing sloj doprinosi oblik domaćin kontrole, sloj objekata doprinosi OBJ zapis čiji common object data deklariše tip objekta 5, a tok zapisa doprinosi sam chart substream. Opcione zastavice na OBJ zapisu su one koje Excel piše za chart okvir: positioned, locked, automatic line i automatic fill, i to je ono što ugrađeni grafikon čini da se ponaša kao nativni kad ga korisnik klikne

HotXLS sidri BIFF8 chart substream za Delphi radni list kroz tri dela koja se slažu: oblik domaćin kontrole iz drawing sloja, OBJ zapis čiji common object data deklariše tip objekta 5, i lanac zapisa grafikona parkiran na kraju toka zapisa lista, gde chart BOF menja kontekst parser-a a zatvarajući EOF ponovo kači zapise
Tri sloja nose jedan ugrađeni grafikon: drawing oblik ga sidri, OBJ zapis mu daje tip domaćina grafikona, a chart substream na kraju toka lista snabdeva zapise koje čitač ponovo kači

Sidro zaslužuje napomenu jer je čest izvor off-by-one bagova. HotXLS API uzima brojeve redova i kolona sa bazom jedan, u skladu sa ostatkom biblioteke, a client anchor upisan u datoteku je sa bazom nula. Konverzija se dešava unutar AddChartObject, pa pozivaoci ostaju u koordinatnom sistemu koji koriste svuda drugde, ali svako ko poredi hex dump sa svojim pozivom treba da se seti koju stranu te granice čita

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;

    // Sidrirano na E2:M20 na ovom listu, sa bazom jedan
    Sheet.AddChartObject(xlsChartTypeColumn, 'Regional sales',
      'Month', 'Amount', Series, 2, 5, 20, 13);

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

FillChar nad nizom serija nije dekoracija. TXLSChartSeriesInfo nosi nekoliko opcionih podzapisa — data labele, stil po seriji, trendline i error bars — svaki vođen boolean-om, i delimično inicijalizovan zapis na steku će emitteru uručiti zastavice koje niko nije postavio. Ispraznite niz, pa postavite polja koja mislite

Koje reference serija prihvata ugrađena putanja?

Obične A1-stilske opsege unutar iste radne sveske, i to ograničenje je namerno, a ne previd. Svaka referenca se razrešava naspram spiska listova sveske i pretvara u indeks eksterne reference koji zapisi grafikona traže. Imenovani opseg ili referenca na eksternu svesku pada nazad na placeholder sa parsiranim izrazom nulte dužine, pa se grafikon piše čisto, ali ta konkretna serija nema izvor podataka dok je ne usmerite na opseg

HotXLS prihvatanje referenci serija na ugrađenoj BIFF8 chart putanji: A1-stilski opsezi poput Data!$B$2:$B$13 unutar iste radne sveske razrešavaju se naspram spiska listova u indeks eksterne reference koji zapisi grafikona traže, dok imenovani opsezi i reference na eksterne sveske padaju na placeholder sa parsiranim izrazom nulte dužine, uz oba pokrivena preko AddChartSheet
Samo obični A1-stilski opsezi unutar iste radne sveske se kompajliraju u reference serija grafikona; sve ostalo se piše čisto kao placeholder dok se ne preusmeri, a puna putanja živi na AddChartSheet

Razlog je direktan inženjerski kompromis. Puna putanja kompajliranja referenci postoji na chart-sheet ruti, umotana unutar sloja kolekcije radnih listova, i njeno čisto izvlačenje značilo bi udvostručavanje sto linija logike razrešavanja za slučaj koji je u praksi redak. Ugrađeni grafikon gotovo uvek plotuje ćelije na svom listu ili srodnom listu podataka. Imenovane i eksterne reference su pokrivene na chart-sheet putanji preko AddChartSheet, pa ništa nije nedostupno, samo se stiže iz drugog ulaza

Sve ostalo u modelu serija radi identično na obe rute. Vezivanje sekundarne ose, stilovi linije, ispune i markera po seriji, trendlines, error bars i data labele svi su deo TXLSChartSeriesInfo i svi se emituju isto, pa definicija grafikona može da se preseli između ugrađenog objekta i chart sheet-a uz promenu samo poziva. Mehanika axis grupa iza zastavice sekundarne ose pokrivena je u članku o sekundarnim osnim grupama pri BIFF upisu

Zašto se naslov grafikona pročitao kao dva znaka?

Zato što je broj znakova prosleđen tamo gde se očekivao broj bajtova, i BIFF Unicode stringovi tu grešku čine lakom za upis a teškom za viđenje. Kratak BIFF Unicode string počinje brojem znakova i bajtom zastavica, a bajt zastavica nosi high-byte bit koji kaže da li je payload po jedan bajt po znaku ili po dva. Pročitajte 16-bitni payload sa brojem znakova kao da je dužina u bajtovima i dobićete tačno pola stringa: serija imenovana Sales vraća se kao Sa, i naslov grafikona se seče isto, jer naslovi i labele serija dele putanju dekodovanja

Ono što ovaj defekt čini zapaženim je što se ponovio tri puta u istoj familiji zapisa: jednom u imenima trendline, jednom u imenima pivot grafikona, i jednom u naslovima grafikona. Svaka pojava delovala je kao svež bag u novoj mogućnosti. Sve tri bile su isto izostavljeno množenje. Pravilo koje ga je konačno zatvorilo je mehaničko i treba ga primenjivati bez procene: kad god čitate jedan od ovih stringova, prvo pogledajte high-byte zastavicu i pomnožite broj znakova širinom payload-a pre nego što dirate bafer. Detalji na nivou zapisa su u članku o dekodovanju broja znakova XLUnicodeString i high-byte zastavice

// Ugrađeni grafikon deli drawing sloj sa slikama i oblicima,
// pa postojeći crtež na listu ostaje sačuvan. AddChartObject
// vraća indeks kreiranog objekta
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;

Gde se ugrađeni grafikoni uklopuju naspram alternativa

Postoje tri rute i one odgovaraju na različita pitanja. Ugrađeni objekat grafikona pripada pored svojih podataka na radnom listu i to je ono što većina izveštaja želi. Chart sheet odgovara jednom prezentacionom vizuelnom prikazu i daje vam punu putanju kompajliranja referenci. Čuvanje postojećeg grafikona iz učitane datoteke netaknutog je pravi odgovor kad je radna sveska došla iz Excel-a sa formatiranjem koje niko ne želi da biblioteka ponovo tumači; to ponašanje propuštanja opisano je u članku o sačuvanim ChartML i kombinovanim grafikonima

Pošto ugrađeni grafikon jaše na drawing sloju, koegzistira sa slikama i oblicima na istom listu umesto da ih zamenjuje, i opšti model tog sloja pokriven je u članku o grafikonima, slikama i crtežima u HotXLS-u. Sve tri rute stižu u HotXLS Delphi spreadsheet komponenti, pa je izbor o tome kako izveštaj treba da izgleda, a ne o tome šta biblioteka ume da izrazi

Metodološka tačka je ona vredna čuvanja. Kad mogućnost binarnog formata ima postojeći čitač, gradite pisca naspram čitača, a ne naspram svog čitanja specifikacije. Čitač kodira godine kontakta sa datotekama koje su stvarne aplikacije proizvele, uključujući delove koje specifikacija izlaže labavo, i pisac koji njega zadovolji daleko je verovatnije da zadovolji i Excel