Ibland är leveransen inte ett dokument, det är en bild av en tabell. Ett sammanfattande block i en statusmejl, en renderad KPI-panel i en instrumentpanel, en miniatyr bredvid en sökträff: alla vill ha cellerna och ingen vill ha papper. TXLSCellImageExporter i HotXLS tar en klassisk eller XLSX-cellrektangel och producerar en kompakt PNG eller JPEG utan sidstorlek, marginaler, sidhuvuden eller sidfötter, utskriftstitlar eller sidbrytningar. Upplösning, skala, format och JPEG-kvalitet är konfigurerbara, objekt, hjälplinjer och cellkanter har oberoende omkopplare, bakgrunden kan vara en färg eller transparent, och filsparandet går genom en atomisk ersättning i samma mapp som lämnar ett befintligt mål orört om någonting misslyckas
Orsaken till att detta behöver sin egen exportör i stället för en flagga på utskriftsvägen är att paginering inte är ett valfritt lager du kan slå av. Det är saken sidpipelinen finns för att göra
Varför inte rendera området genom utskriftspipelinen?
För utskriftspipelinen skjuter in en sida mellan dig och cellerna. Pappersstorlek bestämmer hur mycket som ryms, marginaler knuffar innehållet inåt, sidhuvuden och sidfötter ockuperar band du inte bad om, utskriftstitlar upprepar rader du redan har och sidbrytningar delar området. Ett sammanfattande block som råkar sträcka sig över en brytning kommer ut som två bilder med den intressanta raden skuren itu. Du kan kompensera för allt det genom att ställa in en anpassad sidstorlek som exakt matchar området, och folk gör det, men det betyder att räkna om pappersgeometrin varje gång området ändras och det lämnar fortfarande sidhuvudbandet och utskriftstitelslogiken i vägen
Cellexportören mäter rektangeln, allokerar en bitmapp av exakt den storleken, ritar cellerna in i den och kodar. Det finns ingen sida, så det finns inget att konfigurera bort. För fallen där du faktiskt vill ha papper är PDF-exportvägen rätt verktyg och tas upp i artikeln om kalkylblads-PDF-export
Mät innan du renderar
Measure returnerar pixeldimensionerna som de aktuella inställningarna skulle producera utan att koda någonting. Det spelar roll av två skäl. En HTML- eller e-postmall behöver vanligtvis bildens dimensioner innan bilden finns, så att den kan reservera rutan och undvika layoutskifte. Och en tjänst som renderar användarvalda områden behöver ett sätt att avvisa en absurd begäran innan den allokerar för den
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 håller tunna streck skarpa
Exporter.DPI := 96;
Exporter.Scale := 2.0; // utdata med retina-täthet
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 och H är nu kända; reservera layoutrutan innan kodningen
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;
Budgetar, för skalan multiplicerar
MaxPixels och MaxBytes är inte defensiv dekoration. Pixelantalet växer med kvadraten på skalfaktorn och med kvadraten på upplösningskvoten, så ett område som är skäliga 1200 gånger 800 vid 96 DPI blir ungefär 47 megapixlar vid 600 DPI, och en användare som väljer ett helt använt område i stället för ett sammanfattningsblock lägger till ytterligare en storleksordning ovanpå det. Utan ett tak är felläget en allokering processen inte kan uppfylla, vilket fäller vadhelst annat processen höll på med
Med ett tak misslyckas begäran och anroparen får välja: avvisa, minska skalan eller avsmalna området. Det är en mycket bättre position för en rapportserver, och det är samma resonemang bakom de uttryckliga budgetarna i metabildsavkodaren som beskrivs i artikeln om den avgränsade EMF- och WMF-avkodaren
Atomisk ersättning, och varför mappen spelar roll
Save till ett filnamn skriver inte in i målet. Den skriver en temporär fil i samma mapp, kodar in i den och ersätter först därefter målet. Om kodningen misslyckas, om budgeten överskrids på vägen eller om processen dödas finns den tidigare bilden kvar och är fortfarande giltig. En instrumentpanel som regenererar sina rutor enligt schema visar därför aldrig en avkapad PNG, vilket är det vanliga symptomet på en naiv skrivning som öppnar destinationen och börjar strömma
Samma-mapp-detaljen är inte tillfällig. En atomisk ersättning är bara atomisk inom en volym, för över volymer måste operativsystemet kopiera och sedan ta bort, vilket återinför fönstret du försökte stänga. Varsomhelst implementering av detta mönster som lägger sin temporära fil i systemets tempkatalog är inte atomisk på en maskin där utmatningen bor på en annan enhet
Ritningshändelser ritar på den riktiga canvasen
Både områdesexportören och sidexportören exponerar inledande och avslutande ritningshändelser, och de tar emot en fullständig skrivskyddad kontext i stället för bara ett canvashandle. TXLSPagePaintContext bär den levande canvasen, pixelgränserna, sidstorleken i punkter, upplösningen och skalan som faktiskt används, dokumentets sidnummer, sidnumret inom arket, det totala sidantalet, arknamnet och ursprungsarket i både klassisk och XLSX-smak. Det räcker för att rita en vattenstämpel som skalar korrekt, eller en sidstämpel som vet var den befinner sig i körningen
procedure TReportJob.StampDraft(Sender: TObject;
const AContext: TXLSPagePaintContext);
begin
// Skalmedveten, så stämpeln ser likadan ut vid 1x och 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;
Tre beteenden är värda att lita på. Händelserna avfyras exakt en gång per renderad bildruta, inklusive varje bildruta i en flersidig TIFF, så en räknare som ökas i hanteraren är tillförlitlig. De håller sig tysta under mätningen, så en hanterare med en sideffekt körs inte två gånger för en utmatning. Och om den inledande händelsen kastar avfyras den avslutande inte och inga partiella bildbyte skrivs, så ett undantag i din egen ritkod kan inte producera en halvstämplad fil
Att välja formatet
PNG för vad som helst texttungt. JPEG tillämpar en blocktransform som ger synlig ringing runt tunna högkontraststreck, vilket är precis vad cellkanter och liten text är, och artefakterna överlever vid kvalitetsinställningar där ett fotografi ser perfekt ut. JPEG förtjänar sin plats när området domineras av inbäddade fotografier och filstorlek betyder mer än kantåtergivning. Transparenta bakgrunder kräver PNG, eftersom JPEG saknar alfakanal, så en ruta avsedd att sitta på en färgad yta har fattat beslutet åt dig
Om ditt område innehåller sammanslagna celler, kontrollera utmatningen mot arket: sammanslagna regioner interagerar med kolumnbredder på sätt som överraskar folk, och layoutreglerna tas upp i artikeln om sammanslagna celler och rapportmallar. HotXLS läser och skriver XLS, XLSX, ODS och CSV från Delphi och C++Builder utan något Excel-beroende, och hela exportörytan är dokumenterad på produktsidan för HotXLS Delphi spreadsheet component