Teknisk artikkel

Eksportere et Excel-celleområde som ett bilde med HotXLS

Noen ganger er leveransen ikke et dokument, det er et bilde av en tabell. En sammendragsblokk i en status-e-post, et rendret KPI-panel i et dashbord, en miniatyr ved siden av et søkeresultat: alle vil ha cellene og ingen av dem vil ha papir. TXLSCellImageExporter i HotXLS tar et klassisk eller XLSX-cellerectangel og produserer ett kompakt PNG- eller JPEG-bilde uten sidestørrelse, uten marger, uten topptekster eller bunntekster, uten utskriftstitler og uten sideskift. Oppløsning, skala, format og JPEG-kvalitet er konfigurerbare, objekter, rutenett og cellekanter har uavhengige brytere, bakgrunnen kan være en farge eller transparent, og filskrivingen går gjennom en atomisk samme-mappe-erstatning som lar et eksisterende mål være urørt hvis noe feiler

Grunnen til at dette trenger sin egen eksportør snarere enn et flagg på utskriftsveien, er at paginering ikke er et valgfritt lag du kan slå av. Det er tingen sidepipelinen finnes for å gjøre

Hvorfor ikke rendre området gjennom utskriftspipelinen?

Fordi utskriftspipelinen setter en side mellom deg og cellene. Papirstørrelsen avgjør hvor mye som passer, marger skyver innholdet innover, topptekster og bunntekster okkuperer bånd du ikke ba om, utskriftstitler gjentar rader du allerede har, og sideskift deler området. En sammendragsblokk som tilfeldigvis spenner over et skift, kommer ut som to bilder med den interessante raden kuttet i to. Du kan kompensere for alt det ved å sette opp en egendefinert sidestørrelse som matcher området nøyaktig, og folk gjør det, men det betyr å beregne papirgeometrien på nytt hver gang området endres, og det etterlater fortsatt topptekstbåndet og utskriftstittellogikken i veien

Celleeksportøren måler rectangelet, allokerer en bitmap av nøyaktig den størrelsen, tegner cellene inn i den, og koder. Det finnes ingen side, så det finnes ingenting å konfigurere bort. For tilfellene der du faktisk vil ha papir, er PDF-eksportveien det riktige verktøyet og er dekket i artikkelen om regneark-PDF-eksport

TXLSCellImageExporter måler, tegner og koder ett bilde per celleområde mens utskriftspipelinen deler området ved sideskift
Sidepipelinen setter papirgeometri mellom deg og cellene; celleeksportøren har ingen side noe sted i veien

Mål før du renderer

Measure returnerer pikseldimensjonene de nåværende innstillingene ville produsert uten å kode noe. Det betyr noe av to grunner. En HTML- eller e-postmal trenger vanligvis bildedimensjonene før bildet finnes, slik at den kan reservere boksen og unngå layoutskifte. Og en tjeneste som renderer brukervalgte områder trenger en måte å avvise en absurd forespørsel på før den allokerer for 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 tynne streker skarpe
      Exporter.DPI := 96;
      Exporter.Scale := 2.0;             // retina-tetthet 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 nå kjent; reserver layoutboksen før koding
      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;

Budsjetter, fordi skala multipliserer

MaxPixels og MaxBytes er ikke defensiv pynt. Pikselantallet vokser med kvadratet av skalafaktoren og med kvadratet av oppløsningsforholdet, så et område som er fornuftige 1200 ganger 800 ved 96 DPI, blir grovt 47 megapiksler ved 600 DPI, og en bruker som velger et helt brukt område i stedet for en sammendragsblokk, legger en størrelsesorden til på toppen av det. Uten et tak er feilmodusen en allokering prosessen ikke kan tilfredsstille, noe som tar ned hva enn prosessen ellers gjorde

Med et tak feiler forespørselen, og kalleren får velge: avslå, reduser skalaen, eller innsnevre området. Det er en mye bedre posisjon for en rapportserver, og det er den samme resonneringen bak de eksplisitte budsjettene i metafildekoderen beskrevet i artikkelen om den avgrensede EMF- og WMF-dekoderen

Budsjettflyt for TXLSCellImageExporter i HotXLS: Measure returnerer pikselstørrelsen først, deretter avgrenser MaxPixels og MaxBytes allokering og outputstørrelse
Avslag skjer før allokering, og et bytebudsjettfeil lar det forrige bildet være urørt for kalleren

Atomisk erstatning, og hvorfor mappen betyr noe

Save til et filnavn skriver ikke inn i målet. Den skriver en midlertidig fil i den samme mappen, koder inn i den, og erstatter først da målet. Hvis kodingen feiler, hvis budsjettet overskrides underveis, eller hvis prosessen drepes, er det forrige bildet fortsatt der og fortsatt gyldig. Et dashbord som regenererer flisene sine etter en timeplan, viser derfor aldri en avkortet PNG, noe som er det vanlige symptomet på en naiv skriving som åpner destinasjonen og begynner å strømme

Samme-mappe-detaljen er ikke tilfeldig. En atomisk erstatning er bare atomisk innenfor ett volum, fordi på tvers av volumer må operativsystemet kopiere og deretter slette, noe som gjeninnfører vinduet du prøvde å lukke. Enhver implementering av dette mønsteret som legger sin midlertidige fil i systemets temp-katalog, er ikke atomisk på en maskin der outputen bor på en annen stasjon

TXLSCellImageExporter Save koder inn i en temp-fil i samme mappe, og erstatter deretter målet atomisk; feiler lar det forrige bildet være gyldig
Den midlertidige filen må bo ved siden av målet fordi en atomisk erstatning bare fungerer innenfor ett volum

Malehendelser tegner på den ekte canvasen

Både områdeeksportøren og sideeksportøren eksponerer ledende og etterfølgende malehendelser, og de mottar en full skrivebeskyttet kontekst snarere enn bare et canvashåndtak. TXLSPagePaintContext bærer den levende canvasen, pikselgrensene, sidestørrelsen i punkter, oppløsningen og skalaen faktisk i bruk, dokumentets sidenummer, sidenummeret i arket, totalt sideantall, arkenavnet og det opprinnelige regnearket i både klassisk og XLSX-smak. Det er nok til å tegne et vannmerke som skalerer korrekt, eller et sidestempel som vet hvor det er i kjøringen

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // Skala-bevisst, så stempelet ser likt ut 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 atferder er verdt å stole på. Hendelsene utløses nøyaktig én gang per rendret ramme, inkludert hver ramme av en flersides TIFF, så en teller økt i handleren er til å stole på. De forblir stille under måling, så en handler med en bieffekt kjøres ikke to ganger for én output. Og hvis den ledende hendelsen kaster, utløses ikke den etterfølgende hendelsen, og ingen delvise bildebyte skrives, så et unntak i din egen tegnekode kan ikke produsere en halvstemplet fil

Å velge formatet

PNG for alt tekst-tungt. JPEG anvender en blokktransform som produserer synlig ringning rundt tynne høykontraststreker, noe som er nøyaktig hva cellekanter og liten tekst er, og artefaktene overlever ved kvalitetsinnstillinger der et fotografi ser perfekt ut. JPEG fortjener sin plass når området domineres av innebygde fotografier og filstørrelse betyr mer enn kanttrohet. Transparente bakgrunner krever PNG, siden JPEG ikke har noen alfakanal, så en flis ment til å sitte på en farget flate har tatt valget for deg

Hvis området ditt inneholder sammenslåtte celler, sjekk outputen mot arket: sammenslåtte regioner samhandler med kolonnebredder på måter som overrasker folk, og layoutreglene er dekket i artikkelen om sammenslåtte celler og rapportmaler. HotXLS leser og skriver XLS, XLSX, ODS og CSV fra Delphi og C++Builder uten Excel-avhengighet, og hele eksportørflaten er dokumentert på produktsiden for HotXLS Delphi spreadsheet component