Teknisk artikel

Exportera ett Excel-cellområde som en bild med HotXLS

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

TXLSCellImageExporter mäter, ritar och kodar en bild per cellområde medan utskriftspipelinen delar området vid sidbrytningar
Sidpipelinen skjuter in pappersgeometri mellan dig och cellerna; cellexportören har ingen sida någonstans i vägen

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

Budgetflöde för TXLSCellImageExporter i HotXLS: Measure returnerar pixeldimensionen först, sedan avgränsar MaxPixels och MaxBytes allokering och utdatastorlek
Avvisande händer innan allokering, och ett bytebudgetsfel lämnar den tidigare bilden orörd för anroparen

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

TXLSCellImageExporter Save kodar in i en tempfil i samma mapp och ersätter sedan målet atomiskt; misslyckanden lämnar den tidigare bilden giltig
Den temporära filen måste bo bredvid målet eftersom en atomisk ersättning bara fungerar inom en volym

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