Tehnički članak

HotXLS grafikoni i slike: Excel crteži u Delphiju

Sve što pluta iznad mreže radnog lista (grafikon, logotip, pečat, oblačić) je objekat za crtanje, a objekat za crtanje definisan je dvema stvarima: šta je i gde je usidren. Sidro (anchor) je deo u kojem ljudi greše. Grafikon ne živi u ćeliji; on se nalazi u pravougaoniku prikovanom za raspon redova i kolona, a podaci koje prikazuje zaseban su skup A1 referenci o kojima sidro ne zna ništa. Pomaknite okvir i prikaz grafikona ostaje na istom mestu. Umetnite redove ispod njega i okvir klizi prema dole zajedno sa njima. Održavanje ta dva koordinatna sistema usklađenim je većina onoga što kod za crtanje čini funkcionalnim

HotXLS je izvorna biblioteka za Object Pascal koja čita i zapisuje XLS i XLSX bez Excel automatizacije, i nosi dva zasebna modela crtanja jer ta dva formata datoteka čuvaju crteže različito. Format BIFF8 .xls drži grafikone na sopstvenim namenskim listovima, a plutajuće oblike u OfficeArt toku pridruženom radnom listu. Format OOXML .xlsx može da ugradi grafikon direktno u mrežu, usidren za pravougaonik ćelije, zajedno sa istom vrstom plutajućih slika i oblika. Model objekta odražava tu podelu, a greške o kojima vredi pisati proizlaze iz primene pravila jednog formata na drugi

Koji kontejner može šta da sadrži

Odabir kontejnera (container) mora da prethodi bilo kom kodu grafikona, jer se dostupni tipovi objekata razlikuju između njih:

  • XLS (BIFF8): grafikoni žive na namenskim listovima grafikona stvorenim putem AddChartSheet na kolekciji Sheets. Slike, tekstualni okviri, pravougaonici, ovali i linije su OfficeArt oblici kojima se upravlja putem kolekcije Shapes radnog lista. Ne postoji API za ugradnju grafikona unutar normalne mreže radnog lista
  • XLSX (OOXML): grafikoni se mogu ugraditi direktno u radni list pomoću TXLSXWorksheet.AddChart, usidreni za pravougaonik ćelije, ili postaviti na namenski list grafikona pomoću TXLSXWorkbook.AddChartSheet. Slike se unose sa AddImage ili AddImageFromFile, a plutajuće oznake sa AddTextBox

Dakle, zahtev sročen kao 'kontrolna tabla sa grafikonom pored brojeva' zapravo je zahtev za .xlsx format. U .xls formatu to možete samo približno oponašati guranjem grafikona na sopstveni list, što menja način na koji korisnik navigira datotekom i kako se vaš kod mora ponašati. List koji vraća AddChartSheet na XLS strani je podtok grafikona, a ne mreža: pisanje na njega pomoću Cells.Item proizvodi nekonzistentan tok crtanja koji se generiše bez greške, a koji Excel zatim odbacuje pri otvaranju. Grafikon jednostavno nestane, a ništa u zapisniku izgradnje ne govori zašto. Tretirajte vraćeni list kao list isključivo za grafikon i cela klasa prijava o 'nestalim grafikonima' će nestati

Ugradnja grafikona u XLSX radni list

XLSX staza je ona koja daje prostor za manevrisanje, i tu dva koordinatna sistema iz uvoda postaju konkretna. Pravougaonik sidra prosleđen u AddChart izražen je u redovima i kolonama radnog lista i fiksira mesto gde sedi okvir grafikona. Podaci serije izraženi su kao apsolutne A1 reference koje uključuju naziv lista. Oni su nezavisni: možete pomeriti okvir na drugi kraj lista, a grafikon će i dalje prikazivati iste ćelije

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Chart: TXLSXChart;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Sheet.Cells[1, 1].Value := 'Region';
    Sheet.Cells[1, 2].Value := 'Revenue';
    Sheet.Cells[2, 1].Value := 'East';
    Sheet.Cells[2, 2].Value := 1184350;
    Sheet.Cells[3, 1].Value := 'Central';
    Sheet.Cells[3, 2].Value := 902210;
    Sheet.Cells[4, 1].Value := 'West';
    Sheet.Cells[4, 2].Value := 1010675;

    // Frame anchored to rows 6..22, columns 1..8
    Chart := Sheet.AddChart(xlsxChartColumn, 'Revenue by Region', 6, 1, 22, 8);
    Chart.AddSeries('Revenue', 'Sales!$A$2:$A$4', 'Sales!$B$2:$B$4');
    Chart.ValueAxisTitle := 'USD';

    Sheet.AddImageFromFile(1, 5, 'logo.png');
    Book.SaveAs('dashboard.xlsx');
  finally
    Book.Free;
  end;
end;

Argument koji stvara probleme je niz raspona prosleđen u AddSeries. To je doslovna vrednost (literal), snimljena u trenutku poziva, i ona nema pojma da biste nakon toga mogli dodati još dvadeset redova podataka. Izgradite ga na osnovu broja redova koji ste izračunali nakon što su podaci zapisani, nikada pre. Raspršeni (scatter) i mehuričasti (bubble) grafikoni preopterećuju ista dva argumenta sa različitim značenjima: raspon kategorija sada daje X vrednosti, a raspon vrednosti daje Y, dok poluprečnik mehurića dolazi iz trećeg referentnog skupa postavljenog putem BubbleSizeRange na vraćenom objektu TXLSXChartSeries. Čitajte poziv kao 'X, Y, veličina' umesto 'kategorije, vrednosti' čim napustite familiju stubičastih i trakastih grafikona

TXLSXChartType obuhvata stubičaste, trakaste, linijske, tortne, površinske, prstenaste, raspršene, mehuričaste i radarske grafikone, što pokriva svakodnevni repertoar izveštavanja. Za grafikon preko cele stranice bez okružujuće mreže, Book.AddChartSheet vraća list čije je svojstvo IsChartSheet postavljeno na true. To je XLSX pandan nasleđenom listu grafikona i nosi isto očekivanje: nemojte pisati sadržaj ćelija na njega

Slike se unose kao bajtovi i dimenzionišu se u EMU jedinicama

Postoje dva preopterećenja (overloads) za umetanje slike, a njihovo mešanje je bug sa slikama koji se najčešće pojavljuje u recenziji koda. Funkcija AddImage(ARow, ACol, AData, AFormat) zahteva već kodirane bajtove slike u parametru AData: sirovi sadržaj PNG, JPEG, GIF ili BMP datoteke. Prosledite joj putanju datoteke i sačuvaćete niz od četrdeset bajtova koji nijedan pregledač ne može da dekodira, što je tačno ona prijava o slomljenoj ikoni slike koju ne želite da ispravljate nakon implementacije. Kada je izvor datoteka na disku, radije pozovite AddImageFromFile i pustite biblioteku da pročita bajtove i sama klasifikuje format

Zatim dolazi dimenzonisanje. DrawingML ne meri u pikselima; meri u engleskim metričkim jedinicama (English Metric Units), gde 914400 EMU čini jedan inč, a pri 96 DPI, 9525 EMU čini jedan piksel. Objekat TXLSXImage izlaže WidthEMU and HeightEMU, pa logotip koji bi trebao da se prikaže u veličini 180 sa 60 piksela treba 1714500 sa 571500 EMU. Stavite tu pretvorbu u imenovanu konstantu i računajte prema njoj. Magični brojevi poput 1714500 razbacani po kodu su nečitljivi i tiho pogrešni čim neko promeni ciljani DPI. Sidreni red i kolona su, usput, na bazi 1, što odgovara ostatku API-ja za ćelije, a ne EMU matematici koja je na bazi 0

Listovi grafikona i oblici u nasleđenim XLS datotekama

Na strani BIFF8, bogatije preopterećenje AddChartSheet prima tip grafikona, naslove osa i otvoreni niz zapisa TXLSChartSeriesInfo, gde svaki zapis sadrži naziv te raspon kategorija i vrednosti kao nizove karaktera. Plutajući oblici su zasebna stvar: oni idu na sam radni list sa podacima, kroz njegovu kolekciju Shapes, a ne na list grafikona

var
  Book: IXLSWorkbook;
  Data, Trend: IXLSWorksheet;
  Series: array[0..0] of TXLSChartSeriesInfo;
begin
  Book := TXLSWorkbook.Create;   // interface-counted: do not Free
  Data := Book.Sheets.Add;
  Data.Name := 'Data';
  Data.Cells.Item[1, 1].Value := 'Month';
  Data.Cells.Item[1, 2].Value := 'Units';
  Data.Cells.Item[2, 1].Value := 'Apr';
  Data.Cells.Item[2, 2].Value := 1530;
  Data.Cells.Item[3, 1].Value := 'May';
  Data.Cells.Item[3, 2].Value := 1721;

  Series[0].Name := 'Units';
  Series[0].Categories := 'Data!$A$2:$A$3';
  Series[0].Values := 'Data!$B$2:$B$3';
  Trend := Book.Sheets.AddChartSheet('Trend', xlsChartTypeLine,
    'Units sold', 'Month', 'Units', Series);
  // Trend is a chart substream: never call cell methods on it

  Data.Shapes.AddTextBox('Source: ERP nightly export', 6, 1, 8, 4);
  Data.Shapes.AddPicture('approved-stamp.bmp');
  Book.SaveAs('trend.xls');
end;

Ovde su važna dva detalja o životnom veku, i oni vuku u suprotnim smerovima. TXLSWorkbook se drži putem interfejsa IXLSWorkbook i ima brojanje referenci, pa ručno pozivanje Free na njemu pokreće dvostruko oslobađanje. TXLSXWorkbook iz prethodnih odeljaka običan je objekat i mora se osloboditi unutar bloka try..finally. Isti recenzent koda koji označi nedostatak poziva Free na XLSX strani, mora da označi njegovu prisutnost na XLS strani, što je stvarna opasnost od spoticanja kada radite u oba formata unutar iste jedinice. Sami pomagači za oblike su ujednačeni: AddRectangle, AddOval i AddLine, sa DeleteInRange za čišćenje regije crteža, a svi se sidre preko parova redova i kolona, tako da šablon koji umeće redove iznad njih pomiče te oblike zajedno sa mrežom

Još jedno svojstvo opravdava svoje postojanje na nasleđenim datotekama. TXLSPicture.TransparentColor maskira odabranu pozadinsku boju iz bitmape, što je način na koji postavljate nepravougaoni pečat (npr. pečat 'Odobreno' ili vodeni žig) preko mreže u formatu čije BIFF renderovanje nikada nije naučilo PNG alfu. Postavite boju prema kojoj je pečat kreiran i okolni pravougaonik nestaje

Boje tema ne preživljavaju BIFF8 povratno putovanje

OOXML ispune crteža mogu pokazivati na slot boje teme (theme color slot), zbog čega je ponovno bojenje cele .xlsx datoteke zamenom njene teme jeftino. BIFF8 zapisi crteža nemaju takav slot. Kada HotXLS primeni boju teme na XLS crtež, on razrešava boju u doslovnu RGB vrednost i to čuva; indeks teme iz kojeg je proizašla nestaje čim se datoteka zapiše, a ponovno otvaranje ga ne može vratiti. To posebno pogađa 'white-label' alate za izveštavanje, one koji rebrendiraju isti generisani dokument za mnoge klijente. Držite preslikavanje tema-u-RGB u sopstvenoj konfiguraciji i ponovo ga primenite svaki put kada generišete dokument, umest da očekujete da ćete ga pročitati nazad iz sačuvane .xls datoteke

Povezana odluka pojavljuje se na strani performansi. XLS fasadi se može reći da u potpunosti preskoči parsiranje sloja crteža kada iz velike nasleđene datoteke želite samo podatke o ćelijama, postavljanjem _DisableGraphics na true, a to skraćuje stvarno vreme kod skupnog čitanja. Kvaka je trajna: knjiga otvorena na taj način nema OfficeArt tok u memoriji, pa njeno čuvanje briše crteže. Zadržite tu zastavicu samo za čitanje analitičkih zadataka. Šira slika o performansama nalazi se u našim zabeleškama o performansama velikih tabela u HotXLS-u

Održavanje sidara stabilnim dok se mreža menja

Izveštaji retko ostaju veličine u kojoj su generisani, i tu se model sidra iz uvoda isplati. Strukturne operacije XLSX fasade (InsertRows, DeleteRows i ekvivalenti za kolone) pomiču zavisne slojeve zajedno sa ćelijama. Spojene regije, hiperveze, komentari, zamrznuta okna, rasponi filtera, uslovni formati, validacije, tabele, definisani nazivi i, za ovu temu, sidra slika i grafikona, svi putuju zajedno. Logotip usidren u 1. redu ostaje na vrhu kada se ispod njega umetne deset redova. Okvir grafikona usidren ispod bloka podataka klizi prema dole kako blok raste. Jedna stvar koja se ne prepisuje je bilo koji niz raspona koji ste snimili kao doslovnu vrednost (literal) pre nego što se umetanje dogodilo, budući da je to samo tekst koji biblioteka nema razloga ponovo da pregleda. To definiše siguran redosled za popunjavanje šablona: najpre zapišite i preoblikujte podatke, a grafikone izradite i slike postavite u zadnjem prolazu, pri čemu je svaki niz raspona izveden iz broja redova koje imate nakon umetanja, a ne pre

Dva manja alata dovršavaju komplet za postavljanje. Metoda TXLSTextBox.SetArea na XLS strani ponovo sidri postojeći tekstualni okvir ili automatski oblik (auto shape) na novi pravougaonik ćelije, što je gore od brisanja i ponovnog stvaranja kada se blok podnožja pomakne. A preopterećenje bitmape za AddPicture prima živu instancu TBitmap sa neobaveznom zastavicom prozirnosti, pa sve što vaš sopstveni VCL kod može da nacrta (merač, mini grafikon ili tip grafikona koji izvorna lista ne nudi) može se utisnuti direktno u list bez prethodnog zapisivanja privremene datoteke

Grafikoni i slike gotovo su uvek završni sloj na već strukturiranom izveštaju, zbog čega pripremni radovi odlučuju hoće li sjesti čisto. Popunjavanje podataka na koje će se grafikon referisati pokriveno je u članku generisanje izveštaja vođeno šablonima, a održavanje mreže stabilnom ispod vaših sidara tema je članka spojene ćelije i kontrola izgleda. Potpuna dokumentacija klasa i metoda nalazi se na stranici proizvoda HotXLS Component