Technický článek

Export oblasti buněk Excelu jako jednoho obrázku v HotXLS

Někdy dodávkou není dokument, ale obrázek tabulky. Souhrnný blok v e-mailu o stavu, vykreslený panel KPI na dashboardu, miniatura vedle výsledku vyhledávání: všechno to chce buňky a nic z toho nechce papír. TXLSCellImageExporter v HotXLS vezme klasický nebo XLSX obdélník buněk a produkuje jeden kompaktní PNG nebo JPEG bez velikosti papíru, bez okrajů, bez hlaviček či zápatí, bez opakovaných titulů tisku a bez zalomení stránek. Rozlišení, měřítko, formát a kvalita JPEG jsou konfigurovatelné, objekty, mřížka a ohraničení buněk mají nezávislé přepínače, pozadí může být barva nebo průhledné a zápis souboru prochází atomickou náhradou ve stejné složce, která ponechá existující cíl nedotčený, pokud cokoli selže

Důvod, proč to potřebuje vlastní exporter, nikoli příznak na tiskové cestě, je, že stránkování není volitelná vrstva, kterou lze vypnout. Je to ta věc, kvůli níž potrubí stránek existuje

Proč nevykreslit oblast tiskovým potrubím?

Protože tiskové potrubí vkládá stránku mezi vás a buňky. Velikost papíru rozhoduje, co se vejde, okraje tlačí obsah dovnitř, hlavičky a zápatí zabírají pásy, o které jste nežádali, tituly tisku opakují řádky, které už máte, a zalomení stránek rozdělí oblast. Souhrnný blok, který náhodou šlapá na zalomení, vyjde jako dva obrázky s zajímavým řádkem rozpůleným. Můžete to všechno kompenzovat nastavením vlastní velikosti stránky přesně odpovídající oblasti, a lidé to dělají, ale to znamená přepočítávat geometrii papíru při každé změně oblasti a v cestě pořád zůstává logika pásu hlavičky a titulů tisku

Exportér buněk změří obdélník, alokuje bitmapu přesně té velikosti, nakreslí do ní buňky a zakóduje. Stránka tu není, takže není co konfigurovat pryč. Pro případy, kdy papír přece jen chcete, je správným nástrojem cesta exportu PDF a je předmětem článku o exportu listů do PDF

TXLSCellImageExporter změří, nakreslí a zakóduje jeden obrázek na oblast buněk, zatímco tiskové potrubí rozdělí oblast na zalomeních stránek
Potrubí stránek vkládá geometrii papíru mezi vás a buňky; exportér buněk nemá v cestě nikde žádnou stránku

Změřte, než vykreslíte

Measure vrací pixelové rozměry, které by aktuální nastavení produkovalo, aniž by cokoli zakódovalo. To má význam ze dvou důvodů. HTML nebo e-mailová šablona obvykle potřebuje rozměry obrázku dříve, než obrázek existuje, aby mohla rezervovat krabici a vyhnout se posunu layoutu. A služba vykreslující oblasti vybrané uživatelem potřebuje způsob, jak odmítnout absurdní žádost dříve, než pro ni alokuje

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 drží tenké tahy 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 jsou nyní známy; rezervujte layout box před kódování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, protože měřítko násobí

MaxPixels a MaxBytes nejsou obranná výzdoba. Počet pixelů roste s druhou mocninou faktoru měřítka a s druhou mocninou poměru rozlišení, takže oblast, která je rozumných 1200 krát 800 při 96 DPI, se stane zhruba 47 megapixely při 600 DPI a uživatel, který vybere celou použitou oblast namísto souhrnného bloku, přidá další řád navrch. Bez stropu je režim selhání alokace, kterou proces nemůže uspokojit, a ta složí cokoli jiného, co ten proces dělal

Se stropem žádost selže a volající si může vybrat: odmítnout, snížit měřítko nebo zúžit oblast. To je mnohem lepší pozice pro reportovací server a je to tentýž důvod, jaký stojí za explicitními rozpočty v dekodéru metasouborů popsaném v článku o ohraničeném dekodéru EMF a WMF

Tok rozpočtu pro TXLSCellImageExporter v HotXLS: Measure nejdřív vrátí pixelovou velikost, poté MaxPixels a MaxBytes omezí alokaci a velikost výstupu
Odmítnutí nastane před alokací a selhání bajtového rozpočtu ponechá předchozí obrázek volajícímu nedotčený

Atomická náhrada a proč na složce záleží

Save do názvu souboru nezapisuje do cíle. Zapíše dočasný soubor ve stejné složce, zakóduje do něj a teprve potom nahradí cíl. Selže-li kódování, překročí-li se rozpočet v půli, nebo je-li proces zabit, předchozí obrázek je stále tam a stále platný. Dashboard obnovující své dlaždice podle plánu proto nikdy neukáže useknuté PNG, což je obvyklý symptom naivního zápisu, který otevře cíl a začne proudit

Detail stejné složky není nahodilý. Atomická náhrada je atomická jen v rámci jednoho svazku, protože napříč svazky musí operační systém kopírovat a pak mazat, čímž znovu vnáší okno, které jste se snažili zavřít. Jakákoli implementace tohoto vzoru, která dává svůj dočasný soubor do systémové složky temp, není atomická na stroji, kde výstup žije na jiném disku

Save v TXLSCellImageExporter zakóduje do dočasného souboru ve stejné složce a poté atomicky nahradí cíl; selhání ponechá předchozí obrázek platný
Dočasný soubor musí žít vedle cíle, protože atomická náhrada funguje jen v rámci jednoho svazku

Události kreslení malují na skutečné plátno

Exportér oblasti i exportér stránek vystavují počáteční a koncové kreslicí události a ty obdrží plný kontext jen pro čtení, nikoli jen handle plátna. TXLSPagePaintContext nese živé plátno, pixelové meze, velikost stránky v bodech, rozlišení a měřítko skutečně používaná, číslo stránky dokumentu, číslo stránky v listu, celkový počet stránek, název listu a původní list v klasické i XLSX podobě. To stačí k nakreslení vodoznaku správně škálujícího nebo razítka stránky, které ví, kde je v běhu

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // S vědomím měřítka, takže razítko vypadá stejně při 1x i 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;

Tři chování stojí za spoléhání. Události se spustí přesně jednou za vykreslený snímek, včetně každého snímku vícestránkového TIFF, takže počítadlo zvyšované v handleru je důvěryhodné. Během měření zůstávají potichu, takže handler s vedlejším efektem neproběhne dvakrát pro jeden výstup. A pokud počáteční událost vyhodí výjimku, koncová událost se nespustí a žádné částečné bajty obrázku se nezapisují, takže výjimka ve vašem vlastním kreslicím kódu nemůže produkovat napůl opatřený soubor

Výběr formátu

PNG na cokoli bohaté na text. JPEG aplikuje blokovou transformaci, která produkuje viditelný ringing kolem tenkých vysoce kontrastních tahů, což přesně jsou ohraničení buněk a malý text, a artefakty přežívají při nastaveních kvality, kde fotografie vypadá dokonale. JPEG si své místo zaslouží, když oblastí dominují vložené fotografie a velikost souboru záleží víc než věrnost hran. Průhledná pozadí vyžadují PNG, protože JPEG nemá alfa kanál, takže dlaždice určená na barevný povrch rozhodla za vás

Obsahuje-li vaše oblast spojené buňky, zkontrolujte výstup proti listu: spojené regiony interagují se šířkami sloupců způsobem, který lidi překvapuje, a pravidla layoutu pokrývá článek o spojených buňkách a šablonách sestav. HotXLS čte a zapisuje XLS, XLSX, ODS a CSV z Delphi a C++Builder bez závislosti na Excelu a kompletní povrch exporteru je dokumentován na stránce produktu HotXLS Delphi spreadsheet component