How can I send a file document to the printer and have it print?
Asked Answered
D

14

92

Here's the basic premise:

My user clicks some gizmos and a PDF file is spit out to his desktop. Is there some way for me to send this file to the printer queue and have it print to the locally connected printer?

string filePath = "filepathisalreadysethere";
SendToPrinter(filePath); //Something like this?

He will do this process many times. For each student in a classroom he has to print a small report card. So I generate a PDF for each student, and I'd like to automate the printing process instead of having the user generated pdf, print, generate pdf, print, generate pdf, print.

Any suggestions on how to approach this? I'm running on Windows XP with Windows Forms .NET 4.

I've found this StackOverflow question where the accepted answer suggests:

Once you have created your files, you can print them via a command line (you can using the Command class found in the System.Diagnostics namespace for that)

How would I accomplish this?

Dolph answered 23/5, 2011 at 22:22 Comment(0)
I
77

You can tell Acrobat Reader to print the file using (as someone's already mentioned here) the 'print' verb. You will need to close Acrobat Reader programmatically after that, too:

private void SendToPrinter()
{
   ProcessStartInfo info = new ProcessStartInfo();
   info.Verb = "print";
   info.FileName = @"c:\output.pdf";
   info.CreateNoWindow = true;
   info.WindowStyle = ProcessWindowStyle.Hidden;

   Process p = new Process();
   p.StartInfo = info;
   p.Start();

   p.WaitForInputIdle();
   System.Threading.Thread.Sleep(3000);
   if (false == p.CloseMainWindow())
      p.Kill();
}

This opens Acrobat Reader and tells it to send the PDF to the default printer, and then shuts down Acrobat after three seconds.

If you are willing to ship other products with your application then you could use GhostScript (free), or a command-line PDF printer such as http://www.commandlinepdf.com/ (commercial).

Note: the sample code opens the PDF in the application current registered to print PDFs, which is the Adobe Acrobat Reader on most people's machines. However, it is possible that they use a different PDF viewer such as Foxit (http://www.foxitsoftware.com/pdf/reader/). The sample code should still work, though.

Idell answered 24/5, 2011 at 5:40 Comment(4)
WaitForInputIdle() has no effect. It seems that p is in idle-Mode after Start(). Only the sleep for 3 seconds allows Adobe to finish the spooling. This might be a problem for large documents.Birthplace
@B.K. I tracked my file by filename by listening spool with a thread. I want to give that link to track the job status form spool (codeproject.com/Articles/74359/…).Interval
info.Verb = "print"; Do have to create it manually? If yes, them how we can create it?Calcifuge
This solution give me error as below: { "Message": "An error has occurred.", "ExceptionMessage": "No application is associated with the specified file for this operation", "ExceptionType": "System.ComponentModel.Win32Exception", }Calcifuge
S
94

Adding a new answer to this as the question of printing PDF's in .net has been around for a long time and most of the answers pre-date the Google Pdfium library, which now has a .net wrapper. For me I was researching this problem myself and kept coming up blank, trying to do hacky solutions like spawning Acrobat or other PDF readers, or running into commercial libraries that are expensive and have not very compatible licensing terms. But the Google Pdfium library and the PdfiumViewer .net wrapper are Open Source so are a great solution for a lot of developers, myself included. PdfiumViewer is licensed under the Apache 2.0 license.

You can get the NuGet package here:

https://www.nuget.org/packages/PdfiumViewer/

and you can find the source code here:

https://github.com/pvginkel/PdfiumViewer

Here is some simple code that will silently print any number of copies of a PDF file from it's filename. You can load PDF's from a stream also (which is how we normally do it), and you can easily figure that out looking at the code or examples. There is also a WinForm PDF file view so you can also render the PDF files into a view or do print preview on them. For us I simply needed a way to silently print the PDF file to a specific printer on demand.

public bool PrintPDF(
    string printer,
    string paperName,
    string filename,
    int copies)
{
    try {
        // Create the printer settings for our printer
        var printerSettings = new PrinterSettings {
            PrinterName = printer,
            Copies = (short)copies,
        };

        // Create our page settings for the paper size selected
        var pageSettings = new PageSettings(printerSettings) {
            Margins = new Margins(0, 0, 0, 0),
        };
        foreach (PaperSize paperSize in printerSettings.PaperSizes) {
            if (paperSize.PaperName == paperName) {
                pageSettings.PaperSize = paperSize;
                break;
            }
        }

        // Now print the PDF document
        using (var document = PdfDocument.Load(filename)) {
            using (var printDocument = document.CreatePrintDocument()) {
                printDocument.PrinterSettings = printerSettings;
                printDocument.DefaultPageSettings = pageSettings;
                printDocument.PrintController = new StandardPrintController();
                printDocument.Print();
            }
        }
        return true;
    } catch {
        return false;
    }
}
Shirashirah answered 19/1, 2017 at 20:34 Comment(2)
I'm trying to use this in a VSTO (Outlook AddIn). Unfortunately in this environment, the pdfium.dll is not found in the x64/x86 folders. I could install them directly in the vsto folder but then I can't serve Outlook 64-bit and 32-bit. What can I do here ?Hodosh
I stumbled here looking for a way to programmatically send a PDF to printer. A note, PDFium Viewer, although still on GIT Hub, has been archived and is no longer supported since 2019. The latest available version is incompatible with .NET Core.Adlar
I
77

You can tell Acrobat Reader to print the file using (as someone's already mentioned here) the 'print' verb. You will need to close Acrobat Reader programmatically after that, too:

private void SendToPrinter()
{
   ProcessStartInfo info = new ProcessStartInfo();
   info.Verb = "print";
   info.FileName = @"c:\output.pdf";
   info.CreateNoWindow = true;
   info.WindowStyle = ProcessWindowStyle.Hidden;

   Process p = new Process();
   p.StartInfo = info;
   p.Start();

   p.WaitForInputIdle();
   System.Threading.Thread.Sleep(3000);
   if (false == p.CloseMainWindow())
      p.Kill();
}

This opens Acrobat Reader and tells it to send the PDF to the default printer, and then shuts down Acrobat after three seconds.

If you are willing to ship other products with your application then you could use GhostScript (free), or a command-line PDF printer such as http://www.commandlinepdf.com/ (commercial).

Note: the sample code opens the PDF in the application current registered to print PDFs, which is the Adobe Acrobat Reader on most people's machines. However, it is possible that they use a different PDF viewer such as Foxit (http://www.foxitsoftware.com/pdf/reader/). The sample code should still work, though.

Idell answered 24/5, 2011 at 5:40 Comment(4)
WaitForInputIdle() has no effect. It seems that p is in idle-Mode after Start(). Only the sleep for 3 seconds allows Adobe to finish the spooling. This might be a problem for large documents.Birthplace
@B.K. I tracked my file by filename by listening spool with a thread. I want to give that link to track the job status form spool (codeproject.com/Articles/74359/…).Interval
info.Verb = "print"; Do have to create it manually? If yes, them how we can create it?Calcifuge
This solution give me error as below: { "Message": "An error has occurred.", "ExceptionMessage": "No application is associated with the specified file for this operation", "ExceptionType": "System.ComponentModel.Win32Exception", }Calcifuge
Y
32

I know the tag says Windows Forms... but, if anyone is interested in a WPF application method, System.Printing works like a charm.

var file = File.ReadAllBytes(pdfFilePath);
var printQueue = LocalPrintServer.GetDefaultPrintQueue();

using (var job = printQueue.AddJob())
using (var stream = job.JobStream)
{
    stream.Write(file, 0, file.Length);
}

Just remember to include System.Printing reference, if it's not already included. Now, this method does not play well with ASP.NET or Windows Service. It should not be used with Windows Forms, as it has System.Drawing.Printing. I don't have a single issue with my PDF printing using the above code.

I should mention, however, that if your printer does not support Direct Print for PDF file format, you're out of luck with this method.

Yetah answered 11/12, 2014 at 18:1 Comment(2)
I have tried this with 'Microsoft Print to PDF' printer, it asks the target file location as expected but the output file is empty, it has a size of zero byte. I called stream.Close() just after writing to stream but it did not helped. Did you have any idea?Butyl
Two things. First, you don't own job.JobStream, so it's a bad idea to reference it with using. Second, as of .NET Framework 4.5 (and all Core), data written to this stream must be in XPS format as a package stream, according to the docs.Intemperance
P
25

The following code snippet is an adaptation of Kendall Bennett's code for printing pdf files using the PdfiumViewer library. The main difference is that a Stream is used rather than a file.

public bool PrintPDF(
            string printer,
            string paperName,
            int copies, Stream stream)
{
    try
    {
        // Create the printer settings for our printer
        var printerSettings = new PrinterSettings
        {
            PrinterName = printer,
            Copies = (short)copies,
        };

        // Create our page settings for the paper size selected
        var pageSettings = new PageSettings(printerSettings)
        {
            Margins = new Margins(0, 0, 0, 0),
        };
        foreach (PaperSize paperSize in printerSettings.PaperSizes)
        {
            if (paperSize.PaperName == paperName)
            {
                pageSettings.PaperSize = paperSize;
                break;
            }
        }

        // Now print the PDF document
        using (var document = PdfiumViewer.PdfDocument.Load(stream))
        {
            using (var printDocument = document.CreatePrintDocument())
            {
                printDocument.PrinterSettings = printerSettings;
                printDocument.DefaultPageSettings = pageSettings;
                printDocument.PrintController = new StandardPrintController();
                printDocument.Print();
            }
        }
        return true;
    }
    catch (System.Exception e)
    {
        return false;
    }
}

In my case I am generating the PDF file using a library called PdfSharp and then saving the document to a Stream like so:

PdfDocument pdf = PdfGenerator.GeneratePdf(printRequest.html, PageSize.A4);
pdf.AddPage();

MemoryStream stream = new MemoryStream();
pdf.Save(stream);
MemoryStream stream2 = new MemoryStream(stream.ToArray());

One thing that I want to point out that might be helpful to other developers is that I had to install the 32 bit version of the Pdfium native DLL in order for the printing to work even though I am running Windows 10 64 bit. I installed the following two NuGet packages using the NuGet package manager in Visual Studio:

  • PdfiumViewer
  • PdfiumViewer.Native.x86.v8-xfa
Pentheus answered 26/4, 2018 at 13:3 Comment(0)
G
10

The easy way:

var pi=new ProcessStartInfo("C:\file.docx");
pi.UseShellExecute = true;
pi.Verb = "print";
var process =  System.Diagnostics.Process.Start(pi);
Goodrich answered 26/12, 2014 at 15:33 Comment(1)
In order to target a specific printer add: pi.Arguments = "PATH_TO_PRINTER" and use pi.Verb = "PrintTo" instead of pi.Verb = "print"Blythebm
B
9

This is a slightly modified solution. The Process will be killed when it was idle for at least 1 second. Maybe you should add a timeof of X seconds and call the function from a separate thread.

private void SendToPrinter()
{
  ProcessStartInfo info = new ProcessStartInfo();
  info.Verb = "print";
  info.FileName = @"c:\output.pdf";
  info.CreateNoWindow = true;
  info.WindowStyle = ProcessWindowStyle.Hidden;

  Process p = new Process();
  p.StartInfo = info;
  p.Start();

  long ticks = -1;
  while (ticks != p.TotalProcessorTime.Ticks)
  {
    ticks = p.TotalProcessorTime.Ticks;
    Thread.Sleep(1000);
  }

  if (false == p.CloseMainWindow())
    p.Kill();
}
Birthplace answered 12/9, 2014 at 11:9 Comment(0)
M
7

System.Diagnostics.Process.Start can be used to print a document. Set UseShellExecute to True and set the Verb to "print".

Mccomas answered 23/5, 2011 at 22:29 Comment(0)
C
2

You can try with GhostScript like in this post:

How to print PDF on default network printer using GhostScript (gswin32c.exe) shell command

Clink answered 23/5, 2011 at 23:0 Comment(0)
N
2

I know Edwin answered it above but his only prints one document. I use this code to print all files from a given directory.

public void PrintAllFiles()
{
    System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo();
    info.Verb = "print";
    System.Diagnostics.Process p = new System.Diagnostics.Process();
    //Load Files in Selected Folder
    string[] allFiles = System.IO.Directory.GetFiles(Directory);
    foreach (string file in allFiles)
    {
        info.FileName = @file;
        info.CreateNoWindow = true;
        info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
         p.StartInfo = info;
        p.Start();
    }
    //p.Kill(); Can Create A Kill Statement Here... but I found I don't need one
    MessageBox.Show("Print Complete");
}

It essentually cycles through each file in the given directory variable Directory - > for me it was @"C:\Users\Owner\Documents\SalesVaultTesting\" and prints off those files to your default printer.

Nunley answered 1/6, 2016 at 14:6 Comment(0)
F
0

this is a late answer, but you could also use the File.Copy method of the System.IO namespace top send a file to the printer:

System.IO.File.Copy(filename, printerName);

This works fine

Fir answered 21/10, 2015 at 13:11 Comment(2)
Could you elaborate a little? The only thing I get is a file written to the hard disk with the name of printerName...Rovelli
For example: File.Copy("myFileToPrint.pdf", "\\myPrintServerName\myPrinterName");Redeem
V
0

You can use the DevExpress PdfDocumentProcessor.Print(PdfPrinterSettings) Method.

public void Print(string pdfFilePath)
{
      if (!File.Exists(pdfFilePath))
          throw new FileNotFoundException("No such file exists!", pdfFilePath);

      // Create a Pdf Document Processor instance and load a PDF into it.
      PdfDocumentProcessor documentProcessor = new PdfDocumentProcessor();
      documentProcessor.LoadDocument(pdfFilePath);

      if (documentProcessor != null)
      {
          PrinterSettings settings = new PrinterSettings();

          //var paperSizes = settings.PaperSizes.Cast<PaperSize>().ToList();
          //PaperSize sizeCustom = paperSizes.FirstOrDefault<PaperSize>(size => size.Kind == PaperKind.Custom); // finding paper size

          settings.DefaultPageSettings.PaperSize = new PaperSize("Label", 400, 600);

          // Print pdf
          documentProcessor.Print(settings);
      }
}
Valarievalda answered 5/9, 2018 at 22:14 Comment(0)
C
0

Windows 10 and above include Windows.Data.Pdf APIs which permit printing and display of PDF files via Direct2D. No additional licenses or software are required, and the API's may be called from desktop and UWP applications.

Rmg.PdfPrinting, a small wrapper which exposes that functionality to .Net, is available on Nuget which permits printing via:

var pdfPrinter = new Rmg.PdfPrinting.PdfPrinter();
await pdfPrinter.Print("Printer Name", "Example.pdf");

If you prefer to avoid the wrapper, P/Invokes to CreateDocumentPackageTargetForPrintJob, PdfCreateRenderer, and a few other native API's are required:

var pdfFile = await StorageFile.GetFileFromPathAsync("example.pdf");
var pdfDoc = await PdfDocument.LoadFromFileAsync(pdfFile);

// Initializing DXGI, D3D, D2D, and WIC are omitted for brevity
// See https://github.com/mgaffigan/D2DPdfPrintSample/blob/master/PrintPdfManaged/Program.cs#L55

// Initialize the job
ID2D1PrintControl printControl;
{
    // Create a factory for document print job.
    var documentTargetFactory = (IPrintDocumentPackageTargetFactory)new PrintDocumentPackageTargetFactory();
    IPrintDocumentPackageTarget docTarget;
    documentTargetFactory.CreateDocumentPackageTargetForPrintJob(
        printerName, jobName, null, ArrayToIStream(ticket), out docTarget);

    // Create a new print control linked to the package target.
    d2dDevice.CreatePrintControl(pWic, docTarget, null, out printControl);
}

// Open the PDF Document
PInvoke.PdfCreateRenderer(dxgiDevice, out var pPdfRendererNative).ThrowOnFailure();
var renderParams = new PDF_RENDER_PARAMS();

// Write pages
for (uint pageIndex = 0; pageIndex < pdfDoc.PageCount; pageIndex++)
{
    var pdfPage = pdfDoc.GetPage(pageIndex);

    d2dContextForPrint.CreateCommandList(out var commandList);
    d2dContextForPrint.SetTarget(commandList);

    d2dContextForPrint.BeginDraw();
    pPdfRendererNative.RenderPageToDeviceContext(pdfPage, d2dContextForPrint, &renderParams);
    d2dContextForPrint.EndDraw();

    commandList.Close();
    printControl.AddPage(commandList, pdfPage.Size.AsD2dSizeF(), null);
}

printControl.Close();

A full sample without the wrapper in C# and C++ are available from Github.

Chadwick answered 14/2 at 16:1 Comment(0)
S
0

After days of trying this and that, we ended up using "PDF Print for .NET" from https://www.winnovative-software.com/. It's the same like Evo PDF's PDF Print but can be purchased as a standalone product (Evo PDF is a whole suite which is more expensive). It's $250 / $550, depending on the license you need and it's the only one I found that does what we want, which looks so trivial, right: Print a PDF silently (no window popping up) & let us have control of scaling.

EDIT: No support reaction from Winnovative to three emails I sent, so maybe not recommendable for that reason!

private static void PrintDirect(string filePath, string printerName)
{
  var printer = new PdfPrint
  {
    LicenseKey = "xyz",
    UseHardMargins = false,
    ShowStatusDialog = false
  };
  printer.PrinterSettings.PrinterName = printerName;
  printer.DefaultPageSettings.Margins = new Margins(20, 20, 20, 20);
  printer.Print(filePath);
}

What I tried (I don't remember everything though):

  1. System.IO.Copy : Works on some printers, prints super fast but is scaled incorrectly by HP printers (lines missing at the bottom). We found no way to control this.
  2. Pdfium: I gave up trying to find and install the correct packages.
  3. IronPdf: Prints correctly but need around 30-40 seconds even on a fast machine.
  4. IronPrint: Prints fast but super blury.
  5. LPN/LPT: Seemed too complex to install on server and clients if I got it right.
Scyros answered 23/2 at 14:41 Comment(0)
L
-1
    public static void PrintFileToDefaultPrinter(string FilePath)
    {
        try
        {
            var file = File.ReadAllBytes(FilePath);
            var printQueue = LocalPrintServer.GetDefaultPrintQueue();

            using (var job = printQueue.AddJob())
            using (var stream = job.JobStream)
            {
                stream.Write(file, 0, file.Length);
            }
        }
        catch (Exception)
        {

            throw;
        }
    }
Lang answered 29/9, 2020 at 8:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.