1

I'm trying to programmatically print pdfs using c#. I tried different libraries (PostSharp, PDFium, etc.) as well as the adobe SDK. However, I have not been able to find anything about printing and stapling the printouts.

I tried using the PrintTicket object (Microsoft) to set the Stapling property but it's not working. I have verified that I have the right print drivers installed. I am able to staple printouts when I print manually so I'm sure the printer supports it.

I edited the post to include the full code. The PDF library I'm using here is PdfiumViewer. I'm not checking the return value in this code but if I run this, the return value I get gives me "ConflictStatus.ConflictResolved".

        using (var server = new PrintServer("print server name"))
        {
            var printerName = "printer with stapling capabilities name";
            var queues = server.GetPrintQueues();
            using (var queue = queues.FirstOrDefault(x => x.FullName == printerName))
            {
                var printTicket = queue.DefaultPrintTicket;
                printTicket.Collation = Collation.Collated;
                printTicket.Stapling = Stapling.StapleTopLeft;
                queue.UserPrintTicket = printTicket;
                queue.CurrentJobSettings.CurrentPrintTicket = printTicket;
                var ret = queue.MergeAndValidatePrintTicket(queue.UserPrintTicket, printTicket);
                using (var pdfDoc = PdfDocument.Load("path to pdf"))
                {
                    using (var printDoc = pdfDoc.CreatePrintDocument())
                    {
                        printDoc.PrinterSettings.PrinterName = printerName;
                        printDoc.PrinterSettings.ToPage = 2;
                        printDoc.Print();
                    }
                }
            }
        }
J Doh'
  • 63
  • 7
  • Possible duplicate of [Print Pdf in C#](https://stackoverflow.com/questions/5566186/print-pdf-in-c-sharp) – Bushuev Jan 25 '18 at 19:47
  • Try calling `queue.MergeAndValidatePrintTicket(queue.UserPrintTicket, printTicket)` to combine the tickets. Be sure to check the return value. – Mark Benningfield Jan 25 '18 at 20:54
  • Thanks for the response guys. No luck using MergeAndValidate. It seems to nullify the Stapling property. The other thing I notice is that when I call queue.GetPrintCapabilities(printTicket), the StaplingCapability property shows a count of zero eventhough the printer supports stapling (and like I mentioned earlier, I am able to staple documents when I print the document manually through Adobe Reader). Any other thoughts? – J Doh' Jan 26 '18 at 20:53
  • 1
    @h.o.m.a.n, thanks for the tip but this isn't a dupe as I have not been able to find anything online about stapling in adobe. I even looked at the Adobe SDK and unfortunately it doesn't seem to be supported by their IAC framework. The link you posted makes no mention of stapling. – J Doh' Jan 26 '18 at 20:55
  • @JDoh': Assigning the `queue.UserPrintTicket` to your ticket, and then merging it with that ticket, doesn't do anything. – Mark Benningfield Jan 27 '18 at 02:41

1 Answers1

0

I finally found something that works! I got the code from the following: https://www.codeproject.com/Articles/488737/Storing-and-recalling-printer-settings-in-Csharp-n

public static class PrinterUtilities
{
    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern IntPtr GlobalFree(IntPtr handle);

    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern IntPtr GlobalLock(IntPtr handle);

    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern IntPtr GlobalUnlock(IntPtr handle);


    /// <summary>
    /// Grabs the data in arraylist and chucks it back into memory "Crank the suckers out"
    /// </summary>
    /// <param name="printerSettings"></param>
    /// <param name="filename"></param>
    public static void SetDevModeFromFile(PrinterSettings printerSettings, string filename)
    {
        IntPtr hDevMode = IntPtr.Zero;// a handle to our current DEVMODE
        IntPtr pDevMode = IntPtr.Zero;// a pointer to our current DEVMODE
        try
        {
            // Obtain the current DEVMODE position in memory
            hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);

            // Obtain a lock on the handle and get an actual pointer so Windows won't move
            // it around while we're futzing with it
            pDevMode = GlobalLock(hDevMode);

            // Overwrite our current DEVMODE in memory with the one we saved.
            // They should be the same size since we haven't like upgraded the OS
            // or anything.


            var fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            var temparray = new byte[fs.Length];
            fs.Read(temparray, 0, temparray.Length);
            fs.Close();
            fs.Dispose();
            for (int i = 0; i < temparray.Length; ++i)
            {
                Marshal.WriteByte(pDevMode, i, temparray[i]);
            }
            // We're done futzing
            GlobalUnlock(hDevMode);

            // Tell our printer settings to use the one we just overwrote
            printerSettings.SetHdevmode(hDevMode);
            printerSettings.DefaultPageSettings.SetHdevmode(hDevMode);

            // It's copied to our printer settings, so we can free the OS-level one
            GlobalFree(hDevMode);
        }
        catch (Exception)
        {
            if (hDevMode != IntPtr.Zero)
            {
                GlobalUnlock(hDevMode);
                // And to boot, we don't need that DEVMODE anymore, either
                GlobalFree(hDevMode);
                hDevMode = IntPtr.Zero;
            }
        }

    }
}

then calling it in my main class

    private static void PrintTest()
    {
        using (var pdfDoc = PdfDocument.Load("pdf doc path"))
        {
            using (var printDoc = pdfDoc.CreatePrintDocument())
            {
                var currentSettings = printDoc.PrinterSettings;
                SetDevModeFromFile(currentSettings, "bin file that has printer settings path");
                currentSettings.ToPage = 3;
                printDoc.Print();
            }
        }
    }

To use the code above, I had to download the original solution from the link. I then ran the original solution in order to update the staple/finishing settings from the printer dialog and then save the settings to a "bin" file. The issue seems to be that not all the settings are exposed by .net (possibly because these are more printer hardware-specific settings). To open the pdf file, I used PDFiumViewer. I hope this helps someone else.

J Doh'
  • 63
  • 7