Tehnički članak

HotXLS grafikoni i slike: crteži u Excelu u Delphiju

Sve što pluta iznad mreže radnog lista (grafikon, logotip, pečat, oblačić) je objekt za crtanje, a objekt za crtanje definiran je dvjema stvarima: što je i gdje je usidren. Sidro (anchor) je dio u kojem ljudi griješe. Grafikon ne živi u ćeliji; on se nalazi u pravokutniku prikovanom za raspon redaka i stupaca, a podaci koje prikazuje zaseban su skup A1 referenci o kojima sidro ne zna ništa. Pomaknite okvir i prikaz grafikona ostaje na istom mjestu. Umetnite retke ispod njega i okvir klizi prema dolje zajedno s njima. Održavanje ta dva koordinatna sustava usklađenima je većina onoga što kôd za crtanje čini funkcionalnim

HotXLS je izvorna knjižnica za Object Pascal koja čita i zapisuje XLS i XLSX bez Excel automatizacije, i nosi dva zasebna modela crtanja jer ta dva formata datoteka spremaju crteže različito. Format BIFF8 .xls drži grafikone na vlastitim namjenskim listovima, a plutajuće oblike u OfficeArt toku pridruženom radnom listu. Format OOXML .xlsx može ugraditi grafikon izravno u mrežu, usidren za pravokutnik ćelije, zajedno s istom vrstom plutajućih slika i oblika. Model objekta odražava tu podjelu, a pogreške o kojima vrijedi pisati proizlaze iz primjene pravila jednog formata na drugi

Koji spremnik može što sadržavati

Odabir spremnika (container) mora prethoditi bilo kojem kodu grafikona, jer se dostupni tipovi objekata razlikuju između njih:

  • XLS (BIFF8): grafikoni žive na namjenskim listovima grafikona stvorenim putem AddChartSheet na kolekciji Sheets. Slike, tekstualni okviri, pravokutnici, 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 izravno u radni list pomoću TXLSXWorksheet.AddChart, usidreni za pravokutnik ćelije, ili postaviti na namjenski list grafikona pomoću TXLSXWorkbook.AddChartSheet. Slike se unose s AddImage ili AddImageFromFile, a plutajuće oznake s AddTextBox

Dakle, zahtjev sročen kao 'kontrolna ploča s grafikonom pokraj brojeva' zapravo je zahtjev za .xlsx format. U .xls formatu to možete samo približno oponašati guranjem grafikona na vlastiti list, što mijenja način na koji korisnik navigira datotekom i kako se vaš kôd 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 generira bez pogreš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 cijela klasa prijava o 'nestalim grafikonima' će nestati

Ugradnja grafikona u XLSX radni list

XLSX staza je ona koja daje prostor za manevriranje, i tu dva koordinatna sustava iz uvoda postaju konkretna. Pravokutnik sidra proslijeđen u AddChart izražen je u redcima i stupcima radnog lista i fiksira mjesto gdje sjedi okvir grafikona. Podaci serije izraženi su kao apsolutne A1 reference koje uključuju naziv lista. Oni su neovisni: možete pomaknuti 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;
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;
$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 proslijeđen u AddSeries. To je doslovna vrijednost (literal), snimljena u trenutku poziva, i ona nema pojma da biste nakon toga mogli dodati još dvadeset redaka podataka. Izgradite ga na temelju broja redaka koji ste izračunali nakon što su podaci zapisani, nikada prije. Raspršeni (scatter) i mjehuričasti (bubble) grafikoni preopterećuju ista dva argumenta s različitim značenjima: raspon kategorija sada daje X vrijednosti, a raspon vrijednosti daje Y, dok polumjer mjehurića dolazi iz trećeg referentnog skupa postavljenog putem BubbleSizeRange na vraćenom objektu TXLSXChartSeries. Čitajte poziv kao 'X, Y, veličina' umjesto 'kategorije, vrijednosti' čim napustite obitelj stupčastih i trakastih grafikona

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

Slike se unose kao bajtovi i dimenzioniraju se u EMU jedinicama

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

Zatim dolazi dimenzioniranje. DrawingML ne mjeri u pikselima; mjeri u engleskim metričkim jedinicama (English Metric Units), gdje 914400 EMU čini jedan inč, a pri 96 DPI, 9525 EMU čini jedan piksel. Objekt TXLSXImage izlaže WidthEMU i HeightEMU, pa logotip koji bi se trebao prikazati u veličini 180 sa 60 piksela treba 1714500 sa 571500 EMU. Stavite tu pretvorbu u imenovanu konstantu i računajte prema njoj. Čarobni brojevi poput 1714500 razbacani po kodu su nečitljivi i tiho pogrešni čim netko promijeni ciljani DPI. Sidreni redak i stupac 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 naslijeđenim XLS datotekama

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

Ovdje su važna dva detalja o životnom vijeku, i oni vuku u suprotnim smjerovima. TXLSWorkbook se drži putem sučelja IXLSWorkbook i ima brojanje referenci, pa ručno pozivanje Free na njemu pokreće dvostruko oslobađanje. TXLSXWorkbook iz prethodnih odjeljaka običan je objekt i mora se osloboditi unutar bloka try..finally. Isti recenzent koda koji označi nedostatak poziva Free na XLSX strani, mora označiti 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, s DeleteInRange za čišćenje regije crteža, a svi se sidre preko parova redaka i stupaca, tako da predložak koji umeće retke iznad njih pomiče te oblike zajedno s mrežom

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

Boje tema ne preživljavaju BIFF8 povratno putovanje

OOXML ispune crteža mogu pokazivati na utor boje teme (theme color slot), zbog čega je ponovno bojanje cijele .xlsx datoteke zamjenom njezine teme jeftino. BIFF8 zapisi crteža nemaju takav utor. Kada HotXLS primijeni boju teme na XLS crtež, on razrješava boju u doslovnu RGB vrijednost i to sprema; 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 izvješćivanje, one koji rebrandiraju isti generirani dokument za mnoge klijente. Držite preslikavanje tema-u-RGB u vlastitoj konfiguraciji i ponovno ga primijenite svaki put kada generirate dokument, umjesto da očekujete da ćete ga pročitati natrag iz spremljene .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 naslijeđene datoteke želite samo podatke o ćelijama, postavljanjem _DisableGraphics na true, a to skraćuje stvarno vrijeme kod skupnog čitanja. Kvaka je trajna: knjiga otvorena na taj način nema OfficeArt tok u memoriji, pa njezino spremanje briše crteže. Zadržite tu zastavicu samo za čitanje analitičkih zadataka. Šira slika o performansama nalazi se u našim bilješkama o performansama velikih tablica u HotXLS-u

Održavanje sidara stabilnima dok se mreža mijenja

Izvješća rijetko ostaju veličine u kojoj su generirana, i tu se model sidra iz uvoda isplati. Strukturne operacije XLSX fasade (InsertRows, DeleteRows i ekvivalenti za stupce) pomiču zavisne slojeve zajedno s ćelijama. Spojene regije, hiperveze, komentari, zamrznuta okna, rasponi filtara, uvjetni formati, validacije, tablice, definirani nazivi i, za ovu temu, sidra slika i grafikona, svi putuju zajedno. Logotip usidren u 1. retku ostaje na vrhu kada se ispod njega umetne deset redaka. Okvir grafikona usidren ispod bloka podataka klizi prema dolje kako blok raste. Jedna stvar koja se ne prepisuje je bilo koji niz raspona koji ste snimili kao doslovnu vrijednost (literal) prije nego što se umetanje dogodilo, budući da je to samo tekst koji knjižnica nema razloga ponovno pregledavati. To definira siguran redoslijed za popunjavanje predloška: najprije zapišite i preoblikujte podatke, a grafikone izradite i slike postavite u zadnjem prolazu, pri čemu je svaki niz raspona izveden iz broja redaka koje imate nakon umetanja, a ne prije

Dva manja alata dovršavaju komplet za postavljanje. Metoda TXLSTextBox.SetArea na XLS strani ponovno sidri postojeći tekstualni okvir ili automatski oblik (auto shape) na novi pravokutnik ćelije, što je bolje od brisanja i ponovnog stvaranja kada se blok podnožja pomakne. A preopterećenje bitmape za AddPicture prima živu instancu TBitmap s neobaveznom zastavicom prozirnosti, pa sve što vaš vlastiti VCL kôd može nacrtati (mjerač, mini grafikon ili tip grafikona koji izvorna lista ne nudi) može se utisnuti izravno u list bez prethodnog zapisivanja privremene datoteke

Grafikoni i slike gotovo su uvijek završni sloj na već strukturiranom izvješću, zbog čega pripremni radovi odlučuju hoće li sjesti čisto. Popunjavanje podataka na koje će se grafikon referirati pokriveno je u članku generiranje izvješća vođeno predlošcima, 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