Technisch artikel

Excel-celbereik als één afbeelding exporteren met HotXLS

Soms is het eindproduct geen document, maar een afbeelding van een tabel. Een samenvattingsblok in een statusemail, een gerenderd KPI-paneel in een dashboard, een miniatuur naast een zoekresultaat: ze willen allemaal de cellen en geen van allemaal wil papier. TXLSCellImageExporter in HotXLS neemt een klassieke of XLSX-celrechthoek en produceert één compacte PNG of JPEG zonder paginagrootte, zonder marges, zonder kop- of voetteksten, zonder afdruktitels en zonder paginabreuken. Resolutie, schaal, formaat en JPEG-kwaliteit zijn instelbaar, objecten, rasterlijnen en celranden hebben onafhankelijke schakelaars, de achtergrond kan een kleur of transparant zijn, en het wegschrijven van het bestand verloopt via een atomair vervangen binnen dezelfde map dat een bestaand doel onaangeroerd laat als er iets faalt

De reden dat dit een eigen exporter nodig heeft in plaats van een vlag op het afdrukpad, is dat paginering geen optionele laag is die u kunt uitschakelen. Het is precies wat de paginapijplijn bestaat om te doen

Waarom het bereik niet via de afdrukpijplijn renderen?

Omdat de afdrukpijplijn een pagina tussen u en de cellen zet. Het papierformaat bepaalt hoeveel er past, marges duwen de inhoud naar binnen, kop- en voetteksten bezetten banden die u niet vroeg, afdruktitels herhalen rijen die u al heeft, en paginabreuken splitsen het bereik. Een samenvattingsblok dat toevallig over een breuk heen valt komt eruit als twee afbeeldingen met de interessante rij doormidden. U kunt al dat compenseren door een eigen paginagrootte in te stellen die exact bij het bereik past, en mensen doen dat, maar dat betekent de papiergeometrie opnieuw berekenen telkens als het bereik verandert, en het laat de kopband en de afdruktitel-logica nog steeds in het pad

De celexporter meet de rechthoek, wijst een bitmap van exact die grootte toe, tekent de cellen erin en codeert. Er is geen pagina, dus er is niets weg te configureren. Voor de gevallen waarin u wél papier wilt, is het PDF-exportpad het juiste gereedschap en dat wordt behandeld in het artikel over PDF-export van werkbladen

TXLSCellImageExporter meet, tekent en codeert één afbeelding per celbereik terwijl de afdrukpijplijn het bereik op paginabreuken splitst
De paginapijplijn zet papiergeometrie tussen u en de cellen; de celexporter heeft nergens in het pad een pagina

Meet voordat u rendert

Measure geeft de pixerafmetingen terug die de huidige instellingen zouden opleveren, zonder iets te coderen. Dat doet er om twee redenen toe. Een HTML- of e-mailtemplate heeft gewoonlijk de afmetingen van de afbeelding nodig voordat de afbeelding bestaat, zodat die de box kan reserveren en layout shift kan vermijden. En een dienst die door gebruikers gekozen bereiken rendert heeft een manier nodig om een absurd verzoek te weigeren voordat er voor wordt toegewezen

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 houdt dunne lijnen scherp
      Exporter.DPI := 96;
      Exporter.Scale := 2.0;             // uitvoer met retina-dichtheid
      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 en H zijn nu bekend; reserveer de layout-box vóór het coderen
      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;

Budgets, want schaal vermenigvuldigt

MaxPixels en MaxBytes zijn geen defensieve versiering. Het pixeraantal groeit met het kwadraat van de schaalfactor en met het kwadraat van de resolutieverhouding, dus een bereik dat bij 96 DPI een redelijke 1200 bij 800 is, wordt bij 600 DPI grofweg 47 megapixels, en een gebruiker die een hele gebruikte range selecteert in plaats van een samenvattingsblok voegt daar nog een orde van grootte bovenop toe. Zonder cap is de faalmodus een toewijzing die het proces niet kan waarmaken, en die neemt mee wat het proces verder deed

Met een cap faalt het verzoek en kan de aanroeper kiezen: weigeren, de schaal verlagen of het bereik versmallen. Dat is een veel betere positie voor een rapportserver, en het is dezelfde redenering achter de expliciete budgets in de metafile-decoder die wordt beschreven in het artikel over de begrensde EMF- en WMF-decoder

Budgetstroom voor TXLSCellImageExporter in HotXLS: Measure geeft eerst de pixergrootte, daarna begrenzen MaxPixels en MaxBytes toewijzing en uitvoergrootte
Weigering gebeurt vóór toewijzing en een falend bytebudget laat de vorige afbeelding onaangeroerd voor de aanroeper

Atomair vervangen, en waarom de map er toe doet

Save naar een bestandsnaam schrijft niet in het doel. Die schrijft een tijdelijk bestand in dezelfde map, codeert daarin en vervangt pas daarna het doel. Als het coderen faalt, als het budget halverwege wordt overschreden of als het proces wordt gedood, is de vorige afbeelding er nog en nog geldig. Een dashboard dat zijn tegels op een schema regenereert toont daarom nooit een afgekapte PNG, wat het gebruikelijke symptoom is van een naïeve schrijfactie die het doel opent en begint te streamen

Het detail van dezelfde map is niet toevallig. Atomair vervangen is alleen atomair binnen één volume, want over volumes heen moet het besturingssysteem kopiëren en dan verwijderen, wat het venster dat u wilde sluiten opnieuw introduceert. Elke implementatie van dit patroon die zijn tijdelijke bestand in de systeem-tempmap zet, is niet atomair op een machine waar de uitvoer op een andere schijf staat

Save van TXLSCellImageExporter codeert naar een tijdelijk bestand in dezelfde map en vervangt daarna atomair het doel; falen laat de vorige afbeelding geldig
Het tijdelijke bestand moet naast het doel staan want atomair vervangen werkt alleen binnen één volume

Paint-events tekenen op de echte canvas

Zowel de range-exporter als de pagina-exporter stellen voorafgaande en afsluitende paint-events bloot, en ze ontvangen een volledige alleen-lezen context in plaats van slechts een canvas-handle. TXLSPagePaintContext draagt de live canvas, de pixelgrenzen, de paginagrootte in punten, de werkelijk gebruikte resolutie en schaal, het documentpaginanummer, het paginanummer binnen het werkblad, het totaal aantal pagina's, de werkbladnaam en het bronwerkblad in zowel klassieke als XLSX-gedaante. Dat is genoeg om een watermerk te tekenen dat correct meeschaalt, of een paginastempel die weet waar die in de run zit

procedure TReportJob.StampDraft(Sender: TObject;
  const AContext: TXLSPagePaintContext);
begin
  // Schaalbewust, zodat de stempel er bij 1x en 3x hetzelfde uitziet
  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;

Drie gedragingen zijn het vertrouwen waard. De events vuren exact één keer per gerenderde frame, inclusief elk frame van een TIFF met meerdere pagina's, dus een teller die in de handler wordt opgehoogd is betrouwbaar. Ze blijven stil tijdens het meten, dus een handler met een bijwerking draait niet twee keer voor één uitvoer. En als het voorafgaande event gooit, vuurt het afsluitende event niet en worden geen partiële afbeeldingsbytes weggeschreven, dus een exception in uw eigen tekencode kan geen half gestempeld bestand opleveren

Het formaat kiezen

PNG voor alles wat tekstrijk is. JPEG past een bloktransformatie toe die zichtbare rimpeling produceert rond dunne hoogcontrasterende lijnen, en dat zijn celranden en kleine tekst precies, en de artefacten overleven bij kwaliteitsinstellingen waarop een foto er perfect uitziet. JPEG verdient zijn plek wanneer het bereik gedomineerd wordt door ingebedde fotos en bestandsgrootte er meer toe doet dan randgetrouwheid. Transparante achtergronden vereisen PNG, want JPEG heeft geen alfakanaal, dus een tegel die op een gekleurd oppervlak moet komen heeft de keuze al voor u gemaakt

Bevat uw bereik samengevoegde cellen, controleer de uitvoer dan tegen het werkblad: samengevoegde gebieden interageren op manieren die mensen verrassen met kolombreedtes, en de lay-outregels worden behandeld in het artikel over samengevoegde cellen en rapportsjablonen. HotXLS leest en schrijft XLS, XLSX, ODS en CSV vanuit Delphi en C++Builder zonder Excel-afhankelijkheid, en het volledige exporterooppervlak staat gedocumenteerd op de productpagina van de HotXLS Delphi spreadsheet component