Odborný článok

Export rozsahu buniek Excelu ako jedného obrázka s HotXLS

Niekedy výstupom nie je dokument, ale obrázok tabuľky. Súhrnný blok v statusovom e-maile, vyrenderovaný panel KPI na dashboarde, miniatúra vedľa výsledku vyhľadávania: všetky chcú bunky a žiadny nepatr papier. TXLSCellImageExporter v HotXLS berie klasický či XLSX obdĺžnik buniek a produkuje jeden kompaktný PNG alebo JPEG bez veľkosti strany, bez okrajov, bez hlavičiek či pätičiek, bez tlačových titulov a bez zlomov strán. Rozlíšenie, mierka, formát a kvalita JPEG sú nastaviteľné, objekty, mriežka a okraje buniek majú nezávislé prepínače, pozadie môže byť farba alebo transparentné a zápis súboru prechádza atomickou zámenou v tom istom priečinku, ktorá ponechá existujúci cieľ nedotknutý, ak čokoľvek zlyhá

Dôvod, prečo to potrebuje vlastný exportér namiesto príznaku na tlačovej ceste, je, že stránkovanie nie je voliteľná vrstva, ktorú možno vypnúť. Je to vec, na ktorú pipeline strán existuje

Prečo nevyrenderovať rozsah cez tlačovú pipeline?

Pretože tlačová pipeline vkladá stranu medzi vás a bunky. Veľkosť papiera rozhoduje, koľko sa zmestí, okraje tlačia obsah dovnútra, hlavičky a pätičky obsadzujú pásy, o ktoré ste nežiadali, tlačové tituly opakujú riadky, ktoré už máte, a zlomy strán rozdeľujú rozsah. Súhrnný blok, ktorý náhodou prekračuje zlom, vyjde ako dva obrázky s zaujímavým riadkom prerezaným na polovicu. Všetko to môžete kompenzovať nastavením vlastnej veľkosti strany presne zodpovedajúcej rozsahu a ľudia to robia, ale to znamená prepočítavať geometriu papiera pri každej zmene rozsahu a aj tak zostáva v ceste pás hlavičky a logika tlačových titulov

Bunkový exportér zmeria obdĺžnik, alokuje bitmapu presne tej veľkosti, nakreslí do nej bunky a zakóduje. Nie je tu strana, takže niet čo konfigurovať preč. Pre prípady, keď papier chcete, je správnym nástrojom cesta exportu PDF a pokrýva ju článok o exporte pracovného hárka do PDF

TXLSCellImageExporter meria, kreslí a kóduje jeden obrázok na rozsah buniek, zatiaľ čo tlačová pipeline rozdeľuje rozsah na zlomoch strán
Pipeline strán vkladá geometriu papiera medzi vás a bunky; bunkový exportór nemá stranu nikde v ceste

Merajte skôr, než vyrenderujete

Measure vracia pixelové rozmery, ktoré by aktuálne nastavenia produkovali, bez kódovania čohokoľvek. To má význam z dvoch dôvodov. HTML či e-mailová šablóna zvyčajne potrebuje rozmery obrazu skôr, než obraz existuje, aby si mohla rezervovať box a vyhnúť sa posunu layoutu. A služba, ktorá renderuje používateľom vybrané rozsahy, potrebuje spôsob, ako odmietnuť absurdnú požiadavku skôr, než alokuje pre ňu

uses
  lxHandleX, lxPagination;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Exporter: TXLSCellImageExporter;
  Summary: TXLSXRange;
  W, H, Bytes: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarter.xlsx');
    Sheet := Book.Sheets.ByPos[0];
    Summary := Sheet.Range['A1:F20'];

    Exporter := TXLSCellImageExporter.Create;
    try
      Exporter.ImageFormat := xpifPng;   // PNG udržuje tenké ťahy ostré
      Exporter.DPI := 96;
      Exporter.Scale := 2.0;             // výstup s hustotou retina
      Exporter.IncludeGridlines := False;
      Exporter.IncludeCellBorders := True;
      Exporter.TransparentBackground := True;
      Exporter.MaxPixels := 40 * 1000 * 1000;
      Exporter.MaxBytes := 8 * 1024 * 1024;

      if not Exporter.Measure(Summary, W, H) then
        raise Exception.Create('range exceeds the configured budget');
      // W a H sú teraz známe; rezervujte layout box pred kódovaním
      Bytes := Exporter.Save(Summary, 'summary.png');
      if Bytes <= 0 then
        raise Exception.Create('image export failed, previous file kept');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

Rozpočty, pretože mierka násobí

MaxPixels a MaxBytes nie sú obranná dekorácia. Počet pixelov rastie s druhou mocninou faktora mierky a s druhou mocninou pomeru rozlíšenia, takže rozsah, ktorý je rozumných 1200 krát 800 pri 96 DPI, sa stane približne 47 megapixelmi pri 600 DPI a používateľ, ktorý vyberie celý použitý rozsah namiesto súhrnného bloku, pridá ešte ďalší rád veľkosti navyše. Bez stropu je režim zlyhania alokácia, ktorú proces nedokáže uspokojiť, čo zvalí všetko ostatné, čo ten proces robil

So stropom požiadavka zlyhá a volajúci si vyberie: odmietnuť, znížiť mierku alebo zúžiť rozsah. To je oveľa lepšia pozícia pre reportový server a je to to isté uvažovanie za explicitnými rozpočtmi v dekóderi metafilov popísanom v článku o ohraničenom dekóderi EMF a WMF

Tok rozpočtu pre TXLSCellImageExporter v HotXLS: Measure najprv vráti veľkosť pixelov, potom MaxPixels a MaxBytes ohraničujú alokáciu a veľkosť výstupu
Odmietnutie sa deje pred alokáciou a zlyhanie bajtového rozpočtu ponechá predchádzajúci obraz nedotknutý pre volajúceho

Atomická zámena a prečo záleží na priečinku

Save na meno súboru nezapisuje do cieľa. Zapisuje dočasný súbor v tom istom priečinku, kóduje do neho a až potom zamení cieľ. Ak kódovanie zlyhá, ak sa rozpočet prekročí v polovici alebo ak je proces zabitý, predchádzajúci obraz je stále tam a stále platný. Dashboard, ktorý regeneruje svoje dlaždice podľa plánu, preto nikdy neukáže skrátený PNG, čo je obvyklý príznak naivity zápisu, ktorý otvorí cieľ a začne streamovať

Detail rovnakého priečinka nie je náhodný. Atomická zámena je atomická len v rámci jedného zväzku, pretože naprieč zväzkami musí operačný systém kopírovať a potom mazať, čo znovu zavádza okno, ktoré ste sa snažili zavrieť. Akákoľvek implementácia tohto vzoru, ktorá dáva svoj dočasný súbor do systémového temp adresára, nie je atomická na stroji, kde výstup býva na inom disku

Save TXLSCellImageExporter kóduje do dočasného súboru v tom istom priečinku, potom atomicky zamení cieľ; zlyhania ponechajú predchádzajúci obraz platný
Dočasný súbor musí bývať vedľa cieľa, pretože atomická zámena funguje len v rámci jedného zväzku

Paint udalosti kreslia na skutočný canvas

Exportér rozsahu aj exportér strán vystavujú vedúce a záverečné paint udalosti a prijímajú úplný kontext len na čítanie, nie len handle canvasu. TXLSPagePaintContext nesie živý canvas, pixelové hranice, veľkosť strany v bodoch, rozlíšenie a mierku skutočne používané, číslo strany dokumentu, číslo strany v hárku, celkový počet strán, meno hárka a pôvodný pracovný hárok v klasickej aj XLSX podobe. To stačí na nakreslenie vodoznaku, ktorý sa správne škáluje, alebo pečiatky strany, ktorá vie, kde je v behu

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // S ohľadom na mierku, takže pečiatka vyzerá rovnako pri 1x a 3x
  AContext.Canvas.Font.Height := Round(-48 * AContext.Scale);
  AContext.Canvas.Font.Color := clSilver;
  AContext.Canvas.Brush.Style := bsClear;
  AContext.Canvas.TextOut(AContext.Bounds.Left + Round(24 * AContext.Scale),
    AContext.Bounds.Top + Round(24 * AContext.Scale), 'DRAFT');
end;

Exporter.AfterPaint := Job.StampDraft;

Tri správania stoja za spoľahnutie. Udalosti sa spustia presne raz za vyrenderovaný rámec, vrátane každého rámca viacstranového TIFF, takže počítadlo zvyšované v handleri je dôveryhodné. Počas merania zostávajú tiché, takže handler s vedľajším efektom nebeží dvakrát na jeden výstup. A ak vedúca udalosť vyhodí výnimku, záverečná udalosť sa nespustí a nezapisujú sa žiadne čiastočné bajty obrazu, takže výnimka vo vašom vlastnom kresliacom kóde nemôže produkovať polopečiatkovaný súbor

Voľba formátu

PNG pre všetko s množstvom textu. JPEG aplikuje blokovú transformáciu, ktorá produkuje viditeľné zvony okolo tenkých vysokokontrastných ťahov, čím presne sú okraje buniek a malý text, a artefakty prežívajú pri nastaveniach kvality, kde fotografia vyzerá perfektne. JPEG si zaslúži svoje miesto, keď rozsahom dominujú vložené fotografie a veľkosť súboru je dôležitejšia než vernosť hrán. Transparentné pozadie vyžaduje PNG, keďže JPEG nemá alpha kanál, takže dlaždica určená sedieť na farebnom povrchu rozhodla za vás

Ak váš rozsah obsahuje zlúčené bunky, skontrolujte výstup voči hárku: zlúčené regióny interagujú so šírkami stĺpcov spôsobmi, ktoré ľudí prekvapujú, a pravidlá layoutu pokrýva článok o zlúčených bunkách a šablónach správ. HotXLS číta a zapisuje XLS, XLSX, ODS a CSV z Delphi a C++Builder bez závislosti na Exceli a kompletný povrch exportéra je zdokumentovaný na produktovej stránke HotXLS Delphi spreadsheet component