Technisch artikel

Een geladen PDF afdrukken in Delphi met HotPDF en TPrinter

HotPDF drukt een geladen PDF af naar een fysieke printer, elke Windows-device context of het klembord via drie aanroepen: PrintLoadedDocument stuurt een VCL-TPrinter aan, RenderLoadedPageToDC tekent een pagina rechtstreeks in elke HDC, en RenderLoadedPageToClipboard kopieert een pagina als een bitmap of een vector-metafile. HotPDF is een native VCL PDF-component voor Delphi en C++Builder, en voegde dit eersteklas afdrukpad toe in v2.331.0, waarna het werd verfijnd in v2.348.0, zodat u niet langer zelf een TPrinter-lus om een bitmap-renderer hoeft te programmeren. Alle drie de aanroepen tekenen de vectorinhoud van de pagina rechtstreeks waar het telt, zodat een afdruk zo scherp blijft als de printer deze kan renderen

Waarom een PDF-pagina rechtstreeks in de printer tekenen in plaats van in een bitmap?

Een PDF-pagina is een reeks vector-tekenoperatoren — paden, tekst en afbeeldingen die worden uitgevoerd tegen het ISO 32000-1 grafische model — en geen raster van pixels. De meest voor de hand liggende manier om een pagina af te drukken is door deze te rasteren naar een bitmap en die bitmap op het printercanvas te blitten. Dit werkt wel, maar gooit het enige weg waar een printer echt goed in is. Een moderne laserprinter heeft een resolutie van 600 of 1200 DPI; a bitmap die gerasterd is op 150 of 300 DPI en vervolgens wordt uitgerekt om te passen, komt wazig aan, met kartelige tekstkanten en zichtbare interpolatie. RenderLoadedPageToDC kiest het betere pad: het speelt de contentstream van de pagina opnieuw af als GDI-aanroepen die rechtstreeks naar de doel-device context worden verzonden, zodat vector-tekst en lijntekeningen de printer bereiken als vectoren en eenmalig door de printer worden gerasterd op de eigen resolutie van de printer

Hoe drukt u in Delphi een geladen PDF af op een fysieke printer?

HotPDF drukt een geladen document af met PrintLoadedDocument(Printer, Options), wat een toegewezen VCL-TPrinter en een THPDFPrintOptions-record accepteert, de geselecteerde pagina's afdrukt en het aantal verzonden pagina's retourneert. Stel eerst Printer.PrinterIndex in zodat een apparaat is gekozen, vul een optierecord in — de class-functie THPDFPrintOptions.Default geeft u verstandige waarden — en roep deze eenmalig aan. De bibliotheek opent de taak met Printer.BeginDoc, doorloopt het paginabereik en verzendt een Printer.NewPage tussen de pagina's, en sluit de taak met Printer.EndDoc, zodat u de levenscyclus van de afdruktaak nooit handmatig hoeft te beheren

uses Printers;

var
  Pdf: THotPDF;
  Opts: THPDFPrintOptions;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('invoice.pdf') > 0 then
    begin
      Printer.PrinterIndex := -1;         // -1 selects the default printer
      Opts := THPDFPrintOptions.Default;  // all pages, 300 DPI, FitPage, colour
      Opts.PageRange := '1,3-5';          // one-based; empty prints every page
      Opts.Copies := 2;
      Pdf.PrintLoadedDocument(Printer, Opts);
    end;
  finally
    Pdf.Free;
  end;
end;

De string PageRange is 1-gebaseerd en leest op dezelfde manier als een afdrukdialoogvenster: '1,3-5' drukt pagina 1 en pagina's 3 tot en met 5 af, en een lege string drukt het hele document af. Copies verwijst rechtstreeks naar Printer.Copies. Wees echter eerlijk tegenover uzelf over Collate: omdat VCL collatie inconsistent blootlegt over Delphi-versies, HotPDF stelt het aantal kopieën in en laat collatie over aan de standaardinstelling van het printerstuurprogramma. Dus wanneer u een gegarandeerde collatievolgorde nodig hebt, stelt u deze in op het stuurprogramma via TPrinterSetupDialog in plaats van erop te vertrouwen dat de vlag dit regelt

Hoe bepalen DPI en FitPage de afdrukschaal?

FitPage en DPI hebben een wisselwerking die het waard is om te begrijpen voordat u levert. Met FitPage op True — de standaardinstelling — leest HotPDF de MediaBox van elke pagina, schaalt deze naar Printer.PageWidth and Printer.PageHeight met behoud van de aspectverhouding, en rendert op de schaal waarmee de pagina het papier vult. In die modus wordt Options.DPI effectief overschreven door de passchaal, omdat de uitvoer moet overeenkomen met de fysieke pagina-afmetingen van de printer. Met FitPage op False rendert HotPDF elke pagina 1:1 op exact Options.DPI, waarbij 72 PDF-eenheden worden gekoppeld aan DPI-apparaatpixels. Dit is wat u wilt wanneer de bronpagina en het papier al even groot zijn en u geeft om een exacte resolutie. Grijp naar FitPage wanneer de grootte van de bronpagina en de papierlade verschillen; kies voor 1:1 wanneer ze overeenkomen en u een deterministische schaal wilt

Twee opties die in v2.348.0 zijn toegevoegd, lossen de afdrukken op die er bijna goed uitzien. Elke fysieke printer heeft een harde marge — een niet-afdrukbare rand die de hardware niet kan bereiken — en standaard landt de pagina-oorsprong daarbinnen, zodat een aflopende pagina een strook van twee randen verliest. Stel IgnoreHardMargin in op True en HotPDF vraagt de PHYSICALOFFSETX en PHYSICALOFFSETY van de printer op, en verschuift vervolgens de oorsprong van het apparaat om die verschuiving teniet te doen, zodat de pagina-oorsprong naar de werkelijke papierhoek verwijst. De tweede optie, AutoRotate, is standaard ingeschakeld: deze leest de MediaBox van elke pagina en, wanneer een pagina breder is dan deze hoog is, kantelt de printer naar liggend, zodat een brede pagina onafgeknipt wordt afgedrukt in plaats van te worden bijgesneden naar staand. AutoRotate treedt terug zodra u Orientation expliciet instelt, dus een bewuste oriëntatiekeuze wint altijd

Hoe rendert u een PDF-pagina in elke willekeurige device context?

RenderLoadedPageToDC(PageIndex, DC, DPI, W, H) is de primitieve onder de afdrukloop, en is op zichzelf al nuttig. PrintLoadedDocument roept deze eenmaal per pagina aan tegen het printercanvas, maar het doel kan elke Windows HDC zijn: een off-screen weheugen-DC, het canvas van een besturingselement in een aangepaste viewer, of een afdrukvoorbeeldoppervlak. PageIndex is 0-gebaseerd, W en H zijn de apparaateenheden van het doeloppervlak, en de renderer doorloopt de /Contents van de pagina en verzendt GDI-aanroepen naar uw DC. Eén bewust gedrag is de moeite waard om te weten: het tekent geen achtergrondvulling. De beller is eigenaar van het oppervlak, dus als u wit papier achter de pagina wilt, vult u de DC zelf voordat u gaat renderen. Dit is precies wat u in staat stelt een pagina over een bestaand canvas heen te leggen wanneer u dat wilt

// Draw page 1 into an off-screen 24-bit surface at 150 DPI
var
  Surface: TBitmap;
begin
  Surface := TBitmap.Create;
  try
    Surface.PixelFormat := pf24bit;
    Surface.SetSize(1275, 1650);           // 8.5 x 11 in at 150 DPI
    Surface.Canvas.Brush.Color := clWhite;
    Surface.Canvas.FillRect(Surface.Canvas.ClipRect);  // caller paints the background
    Pdf.RenderLoadedPageToDC(0, Surface.Canvas.Handle, 150, Surface.Width, Surface.Height);
    // Surface now holds the composited page, ready to blit or save
  finally
    Surface.Free;
  end;
end;

Wanneer u gewoon een afgeronde TBitmap nodig hebt — een miniatuur, een afbeeldingsexport, een voorbeeld dat u wilt cachen — grijp dan naar RenderLoadedPageToBitmap, die wordt behandeld in het begeleidende artikel over PDF-pagina's renderen naar een bitmap. Het verschil is wie eigenaar is van het oppervlak: de bitmap-aanroep alloceert en retourneert een pixelbuffer, terwijl RenderLoadedPageToDC in een device context tekent die u al in bezit hebt, wat precies is wat een printercanvas of een paint-handler u overhandigt. Beide delen dezelfde contentstream-interpreter en embedded-glyph-engine, zodat tekst in beide gevallen met exact dezelfde ingebedde contouren tevoorschijn komt

Hoe plakt u een PDF-pagina in Word of in een afbeeldingseditor?

RenderLoadedPageToClipboard(PageIndex, DPI, AsMetafile) plaatst één pagina op het Windows-klembord, klaar om te worden geplakt in Word, PowerPoint, een e-mail of een afbeeldingseditor. De vlag AsMetafile kiest het formaat, en die keuze is belangrijker dan het lijkt. Geef False door en HotPDF plaatst een 24-bits apparaatbitmap (CF_BITMAP) gerenderd op uw DPI — een vast raster van pixels dat overal kan worden geplakt, maar korrelig wordt wanneer de ontvanger het opschaalt. Geef True door en het plaatst een enhanced metafile (CF_ENHMETAFILE) waarin de tekst- en vectorinhoud van de pagina meereizen als GDI-tekenopdrachten, zodat het geplakte resultaat scherp blijft bij elke zoomfactor en meevloeit naar de grootte die het doeldocument eraan geeft

// Copy page 2 as a scalable vector metafile for pasting into Word
Pdf.RenderLoadedPageToClipboard(1, 300, True);

// Copy the same page as a fixed 300-DPI bitmap instead
Pdf.RenderLoadedPageToClipboard(1, 300, False);

De vuistregel is simpel. Als een persoon de pagina in een document zal plakken en deze mogelijk zal schalen — een figuur in een rapport, een clausule in een conceptcontract — gebruik dan de metafile, omdat vectortekst scherp wordt afgedrukt en ingezoomd. Als de bestemming alleen rastergegevens begrijpt, of als u specifiek een bevroren momentopname op een bekende resolutie wilt, gebruik dan de bitmap. Voor het extraheren van de woorden uit een pagina in plaats van een afbeelding ervan, is dat een heel andere taak, afgehandeld door de tekstextractie-API, die de tekens en hun posities retourneert in plaats van een gerenderde afbeelding

Waar het afdrukpad ophoudt

Alles op deze pagina is Windows-only, en het is de moeite waard om dat duidelijk te vermelden. PrintLoadedDocument, RenderLoadedPageToDC en RenderLoadedPageToClipboard zijn gebouwd op TPrinter, het GDI-device context-model, the Windows-klembord en enhanced metafiles — allemaal Win32- en Win64-constructies. Op andere platformen maakt, bewerkt en leest de rest van HotPDF nog steeds PDF's, maar dit door GDI ondersteunde afdruk- en klembordoppervlak is daar niet beschikbaar. De renderer richt zich bovendien op de gangbare document-subset van tekst, paden en afbeeldingen, waardoor ontwerpproeven met veel verlopen en ICC-beheerde kleuren worden afgedrukt als een getrouwe voorvertoning in plaats van een colorimetrische proef. Dit is dezelfde grens die de bitmap-renderer met zich meedraagt

Voor batch-afdruktaken over veel bestanden, laadt en drukt u elk document achtereenvolgens af en let u op het geheugengebruik: PrintLoadedDocument rendert één pagina tegelijk, maar de geladen documentgrafiek zelf blijft voor de hele taak in het geheugen aanwezig. Wanneer u een afdrukwachtrij aanstuurt over honderden grote bestanden, de streaming Direct File-API voor grote PDF-werkstromen past goed bij het afdrukpad — splits, voeg samen of selecteer eerst de pagina's die u nodig hebt op de schijf, en laad vervolgens alleen het afgeslankte resultaat om af te drukken. Dat is hoe PrintLoadedDocument, RenderLoadedPageToDC en RenderLoadedPageToClipboard worden geleverd als onderdeel van de HotPDF-component voor Delphi en C++Builder — een native VCL-bibliotheek zonder externe DLL-afhankelijkheden die PDF-creatie, bewerking, tekstextractie, rendering en nu ook afdrukken rechtstreeks naar TPrinter, elke device context of het klembord omvat