Nogle gange er det leverede ikke et dokument, det er et billede af en tabel. En opsummeringsblok i en statuse-mail, et renderet KPI-panel i et dashboard, en thumbnail ved siden af et søgeresultat: alle vil have cellerne, og ingen af dem vil have papir. TXLSCellImageExporter i HotXLS tager et klassisk eller XLSX cellerectangel og producerer ét kompakt PNG eller JPEG uden sidestørrelse, uden margener, uden sidehoveder eller sidefødder, uden udskriftstitler og uden sideskift. Opløsning, skala, format og JPEG-kvalitet kan konfigureres, objekter, gitterlinjer og cellekantomridser har uafhængige kontakter, baggrunden kan være en farve eller transparent, og filskrivningen går gennem en atomar erstatning i samme mappe, der efterlader et eksisterende mål urørt, hvis noget fejler
Grunden til, at dette behøver sin egen eksportør frem for et flag på udskriftsvejen, er, at paginering ikke er et valgfrit lag, du kan slå fra. Det er det, sidepipelinen findes for at gøre
Hvorfor ikke rendere området gennem udskriftspipelinen?
Fordi udskriftspipelinen indsætter en side mellem dig og cellerne. Papirstørrelsen afgør, hvor meget der kan være plads til, margener skubber indholdet indad, sidehoveder og sidefødder optager bånd, du ikke bad om, udskriftstitler gentager rækker, du allerede har, og sideskift deler området. En opsummeringsblok, der tilfældigvis straddler et skift, kommer ud som to billeder med den interessante række skåret i halv. Du kan kompensere for det hele ved at oprette en brugerdefineret sidestørrelse, der matcher området eksakt, og folk gør det, men det betyder at genberegne papirgeometri, hver gang området ændrer sig, og det efterlader stadig sidehovedbåndet og udskriftstitel-logikken i vejen
Celleeksportøren måler rectanglet, allokerer en bitmap af præcis den størrelse, tegner cellerne ind i den og enkoder. Der er ingen side, så der er intet at konfigurere væk. Til de tilfælde, hvor du reelt vil have papir, er PDF-eksportvejen det rigtige værktøj og er dækket i artiklen om regnearks-PDF-eksport
Mål, før du renderer
Measure returnerer de pixeldimensioner, de nuværende indstillinger ville give, uden at enkode noget. Det betyder noget af to grunde. En HTML- eller e-mail-skabelon behøver som regel billeddimensionerne, før billedet findes, så den kan reservere boksen og undgå layout shift. Og en tjeneste, der renderer brugervalgte områder, behøver en måde at afvise en absurd anmodning på, før den allokerer til 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 holder tynde streger skarpe
Exporter.DPI := 96;
Exporter.Scale := 2.0; // retina-tætheds-output
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 og H er nu kendt; reserver layoutboksen før enkodning
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;
Budgetter, for skala multiplicerer
MaxPixels og MaxBytes er ikke defensiv udsmykning. Pixelantallet vokser med kvadratet på skalafaktoren og med kvadratet på opløsningsforholdet, så et område, der er en rimelig 1200 gange 800 ved 96 DPI, bliver groft sagt 47 megapixels ved 600 DPI, og en bruger, der vælger en hel brugt rækkevidde i stedet for en opsummeringsblok, lægger endnu en størrelsesorden oven på. Uden et tag er fejltilstanden en allokering, processen ikke kan opfylde, hvilket vælter alt andet, som den proces var i gang med
Med et tag fejler anmodningen, og kalderen kan vælge: afvis, sænk skalaen eller indsnævr området. Det er en langt bedre position for en rapportserver, og det er samme begrundelse bag de eksplicitte budgetter i metafildekoderen beskrevet i artiklen om den afgrænsede EMF- og WMF-dekoder
Atomar erstatning, og hvorfor mappen tæller
Save til et filnavn skriver ikke ind i målet. Den skriver en midlertidig fil i samme mappe, enkoder ind i den og erstatter først derefter målet. Hvis enkodningen fejler, hvis budgettet overskrides undervejs, eller hvis processen dræbes, er det forrige billede stadig der og stadig gyldigt. Et dashboard, der regenererer sine tiles efter en tidsplan, viser derfor aldrig en afkortet PNG, hvilket er det sædvanlige symptom på en naiv skrivning, der åbner destinationen og begynder at streame
Samme mappe-detaljen er ikke tilfældig. En atomar erstatning er kun atomar inden for ét bind, for på tværs af bind må operativsystemet kopiere og derefter slette, hvilket genindfører det vindue, du prøvede at lukke. Enhver implementation af dette mønster, der lægger sin midlertidige fil i systemets temp-mappe, er ikke atomar på en maskine, hvor outputtet bor på et andet drev
Paint-events tegner på den rigtige canvas
Både områdeeksportøren og sideeksportøren eksponerer ledende og afsluttende paint-events, og de modtager en fuld skrivebeskyttet kontekst frem for blot et canvas-handle. TXLSPagePaintContext bærer den levende canvas, pixelgrænserne, sidestørrelsen i punkter, den opløsning og skala, der reelt bruges, dokumentets sidenummer, sidenummeret i arket, det samlede sideantal, arkenavnet og det oprindelige regneark i både klassisk og XLSX-variant. Det er nok til at tegne et vandmærke, der skalerer korrekt, eller et sidestempel, der ved, hvor det er i løbet
procedure TReportJob.StampDraft(Sender: TObject;
const AContext: TXLSPagePaintContext);
begin
// Skalabevidst, så stemplet ser ens ud ved 1x og 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 adfærdsmønstre er værd at stole på. Eventene udløses præcis én gang pr. renderet frame, inklusive hver frame i en multipage TIFF, så en tæller, der øges i handleren, er til at stole på. De forbliver stille under måling, så en handler med en sideeffekt ikke kører to gange for ét output. Og hvis den ledende event kaster, udløses den afsluttende event ikke, og ingen delvise billedbytes skrives, så en exception i din egen tegnekode ikke kan producere en halvstemplet fil
At vælge formatet
PNG til alt tekstandsvægtet. JPEG anvender en bloktransform, der giver synlig ringing omkring tynde kontraststærke streger, hvilket præcis er, hvad cellekantomridser og små tekst er, og artefakterne overlever ved kvalitetsindstillinger, hvor et fotografi ser perfekt ud. JPEG gør sig fortjent, når området domineres af indlejrede fotografier, og filstørrelse tæller mere end kant-trofasthed. Transparente baggrunde kræver PNG, da JPEG ikke har nogen alfakanal, så en tile, der skal sidde på en farvet overflade, har truffet valget for dig
Hvis dit område indeholder flettede celler, så tjek outputtet mod arket: flettede regioner interagerer med kolonnebredder på måder, der overrasker folk, og layoutreglerne er dækket i artiklen om flettede celler og rapportskabeloner. HotXLS læser og skriver XLS, XLSX, ODS og CSV fra Delphi og C++Builder uden Excel-afhængighed, og hele eksportørens overflade er dokumenteret på produktsiden HotXLS Delphi spreadsheet component