Eenvoudig PDF printen in C#

In need of an English translation? Please drop me a note!

Printen in het .Net framework is op zich niet zo lastig. En met de komst van WPF en de bijbehorende XpsDocumentWriter kan bv. handig gebruik gemaakt worden van de FlowDocument klasse en de PrintQueue klasse met bijbehorende CreateXpsDocumentWriter methode. Hiermee kun je zelf je templates opmaken, mergen en printen.

Maar specifiek een aangeboden PDF printen is een stuk lastiger. Met de hand lijkt dit eenvoudig, je start gewoon Adobe Reader of iets dergelijks.

Maar een PDF vanuit code uitprinten is niet eenvoudig. De opmaak binnen PDF’s wordt niet direct door de meeste printers herkend. Op het internet zwerven veel oplossingen rond allemaal met hun eigen voor- en nadelen. Adobe Reader is bv. gratis maar geeft een ‘nag’ screen en er zijn commerciële oplossingen maar die zijn niet bepaald goedkoop.

Voor mijn huidige project heeft het team gekeken naar de mogelijkheid om GhostScript te gebruiken. Dit is een bibliotheek om PDF naar ‘niet-postscript’ om te zetten. We zijn met name geïnteresseerd in PCL, dit kunnen de meeste (HP) laserprinters wel aan. Op GhostScript zit een GNU licentie met beperkt commercieel gebruik.

En wat blijkt, in slechts enkele stappen komt het printen van PDF ook voor .Net ontwikkelaars binnen bereik!

Stap 1: Download GhostScript en installeer deze lokaal. De 32 bits versie is een prettige keuze, vooral in combinatie met de andere benodigdheden. Deze werkt ook met een 64 bits Windows7. Refereer naar de gsdll32.dll in je C# project.

We zijn dus geïnteresseerd in die gsdll32.dll en deze is geschreven in C . Gelukkig heeft Matthew Ephraim al de moeite genomen om deze in C# te verpakken. Deze bibliotheek heet heel toepasselijk GhostScriptSharp. De broncode kan je ophalen bv. op GitHub.

Deze wrapper rond de 32 bits GhostScript dll is heel aardig. Er is via de methode GenerateOutput() de mogelijkheid beschikbaar om een PDF om te zetten naar PCL. Als onderdeel van de instellingen moet wel een ‘Device’ meegegeven worden. Hier heb ik een simpele aanpassing moeten maken op GhostScriptSharp. De GhostscriptDevices enum krijgt nu de extra ljet4 voor PCL support want die ontbrak. Waarom deze in de huidige versie niet aanwezig is, is mij onbekend.

Stap 2: download nu de GhostScriptsharp code, integreer deze in je eigen code en breid de enum even uit met de keuze ‘ljet4’. Zie dat die broncode standaard naar de 32 bits versie van GhostScript refereert.

Als laatste moet nu de, door GhostScript uit de PDF, gegenereerde PCL naar de printer gestuurd worden. Jouw printer dient dus wel PCL te ondersteunen :-).

Hiervoor gebruiken we een voorbeeld op de MSDN van Microsoft zelf. Dit voorbeeld gebruikt de Win32 Spooler Application Programmer Interfaces (APIs) en dit werkt ook prima samen met Windows7. De in het voorbeeld genoemde RawPrinterHelper klasse is prima. Er zitten wel enkele geheugenlekjes in (geen usings op de FileStream en de BinaryReader) dus een kleine aanpassing is gewenst.

Stap 3: Integreer de voorbeeldcode van Microsoft in je project en repareer even de RawPrinterHelper.SendFileToPrinter() methode.

Nu wordt het mogelijk om alles bij elkaar te brengen in één aanroep:

string pdfFileName = "test.pdf";
string tempFileName =
    Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
GhostscriptSettings gss = new GhostscriptSettings
{
  Size = new GhostscriptPageSize() { Native = GhostscriptPageSizes.a4 },
  Device = GhostscriptDevices.ljet4,
  Resolution = new Size(600, 600)
};

GhostscriptSharp.GhostscriptWrapper.GenerateOutput
    (pdfFileName, tempFileName, gss);
RawPrinterHelper.SendFileToPrinter(printerName, tempFileName);
File.Delete(tempFileName);

Eerst stellen we een naam samen voor een tijdelijk bestand. Vervolgens laten we via GhostScript vanuit een aangeboden PDF dit tijdelijk bestand vullen met PCL code (op A4 formaat met een resolutie van 600 dpi). En vervolgens laten we dit PCL bestand ruw naar de printer sturen. En achteraf verwijderen we natuurlijk nog even het tijdelijk bestand.

Dit moet voldoende zijn. Natuurlijk moet deze oplossing uitvoerig getest worden doordat het een heleboel zaken integreert… Maar in onze testopstelling functioneert het heel aardig. Persoonlijk had ik liever niet met een tijdelijk bestand op schijf gewerkt, maar met bijvoorbeeld een memorystream.

Advertenties