Showing posts with label pdf print. Show all posts
Showing posts with label pdf print. Show all posts

Sunday, 23 June 2019

Printing zpl and pdf documents through C#

Hi Friends,

 We have seen ways to print a pdf through C# but printing zpl on zebra printers doesn't seems to work like pdf but here I'm demonstrating a way which can print zpl (*.zpl, on zebra printers) and pdf (*.pdf) or other documents types. This also could be a best option as it doesn't depend on any other third party libraries (open source or paid).

public class BasePrintUtility { private static bool SendRawBytesToPrinter(string szPrinterName, string printerFileName, IntPtr pBytes, Int32 dwCount) { Int32 dwError = 0, dwWritten = 0; IntPtr hPrinter = new IntPtr(0); DOCINFOA di = new DOCINFOA(); bool bSuccess = false; // Assume failure unless you specifically succeed. di.pDocName = printerFileName; di.pDataType = "RAW"; // Open the printer. if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) { // Start a document. if (StartDocPrinter(hPrinter, 1, di)) { // Start a page. if (StartPagePrinter(hPrinter)) { // Write your bytes. bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten); EndPagePrinter(hPrinter); } EndDocPrinter(hPrinter); } ClosePrinter(hPrinter); } // If you did not succeed, GetLastError may give more information // about why not. if (bSuccess == false) { dwError = Marshal.GetLastWin32Error(); } return bSuccess; } // Structure and API declarions: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public class DOCINFOA { [MarshalAs(UnmanagedType.LPStr)] public string pDocName; [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile; [MarshalAs(UnmanagedType.LPStr)] public string pDataType; } [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd); [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool ClosePrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di); [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool EndDocPrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool StartPagePrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool EndPagePrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten); public bool SendDocumentToPrinter(string filePath, PrintSettings printSettings) { // Open the file. FileInfo fileInfo = new FileInfo(filePath); Byte[] bytes = null; bool bSuccess = false; // Your unmanaged pointer. IntPtr pUnmanagedBytes = new IntPtr(0); int nLength; using (FileStream fs = fileInfo.OpenRead()) { //FileStream fs = new FileStream(szFileName, FileMode.Open); // Create a BinaryReader on the file. BinaryReader br = new BinaryReader(fs); // Dim an array of bytes big enough to hold the file's contents. bytes = new Byte[fs.Length]; nLength = Convert.ToInt32(fs.Length); // Read the contents of the file into the array. bytes = br.ReadBytes(nLength); } // Allocate some unmanaged memory for those bytes. pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength); // Copy the managed byte array into the unmanaged array. Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength); // Send the unmanaged bytes to the printer. for (int i = 0; i < printSettings.Copies; i++) { bSuccess = SendRawBytesToPrinter(printSettings.PrinterName, fileInfo.Name, pUnmanagedBytes, nLength); } // Free the unmanaged memory that you allocated earlier. Marshal.FreeCoTaskMem(pUnmanagedBytes); return bSuccess; } public bool SendDocumentToPrinter(byte[] content, int copies, string printerName, string fileName) { bool status = false; IntPtr pUnmanagedBytes; int nLength = content.Length; // Allocate some unmanaged memory for those bytes. pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength); // Copy the managed byte array into the unmanaged array. Marshal.Copy(content, 0, pUnmanagedBytes, nLength); // Send the unmanaged bytes to the printer. for (int i = 0; i < copies; i++) { status = SendRawBytesToPrinter(printerName, fileName, pUnmanagedBytes, nLength); } // Free the unmanaged memory that you allocated earlier. Marshal.FreeCoTaskMem(pUnmanagedBytes); return status; } }

isn't it simple.

Note: This is not optimized code and just  a sample. You must optimize it as your need before using it.

Source: https://github.com/andyyou/SendToPrinter/blob/master/Printer/RawPrinterHelper.cs


Thanks.

Monday, 24 April 2017

Printing PDFs on network printer through c# from application hosted in IIS

Printing on network printers or even in local printers programmatically is always a challenge because of the network printer accessibility through code.

I had a requirement where I need to print pdf on network printer programmatically. I found couple of major issues which is a common issue faced by many people but never got satisfactory or final solution over the internet search. It took couple of days to solve the issues and get the final working solution.

Here are the issues which I faced:
Issue #1: Loading the pdf and sending it to the printer.
During the work I got an article which is well explained about the problem of reading pdf and it has couple of alternative solutions provided but this wasn't a satisfactory solution as our client has do the another software installation as pdf reader.
http://www.bincsoft.com/blog/background-printing-of-pdf-documents/

Here I found PdfiumViewer which is an open source provided under Apache licence which helps to read/modify the pdfs programmatically and client/server doesn't need to have anything new installation.

And here is the code to read the pdf and send it to the printer:


public void SendPdfToPrinter(string filePath, string fileName, string printerNetworkPath)
        {
            // Create the printer settings for our printer
            var printerSettings = new System.Drawing.Printing.PrinterSettings
            {
                PrinterName = printerNetworkPath,
                PrintFileName = fileName,
                PrintRange = System.Drawing.Printing.PrintRange.AllPages,
            };
            printerSettings.DefaultPageSettings.Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0);

            // Now print the PDF document
            using (var document = PdfiumViewer.PdfDocument.Load(filePath))
            {
                using (var printDocument = document.CreatePrintDocument())
                {
                    printDocument.DocumentName = fileName;
                    printDocument.PrinterSettings = printerSettings;
                    printDocument.PrintController = new System.Drawing.Printing.StandardPrintController();
                    printDocument.Print();
                }
            }
        }




Here PdfiumViewer.PdfDocument.Load() provides overloaded method to create a pdfdocument from Stream as well.

Issue #2: Printer accessibility issue.
if your application's application pool identity uses any of the built-in accounts (i.e. LocalSystem, LocalService etc) then you will face a issue of accessing the printer. Because your printers doesn't support these users.
There are two possible solutions to overcome this problem:
  • Set Application Pool identity to a custom user (domain user) which has accessibility to the printers.
  • Use impersonation to impersonate a user and print. below is the code to achieve the impersonation.

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public class Impersonation : IDisposable
    {
        private readonly SafeTokenHandle _handle;
        private readonly WindowsImpersonationContext _context;
        bool disposed = false;

        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_INTERACTIVE = 2;

        public Impersonation(ImpersonateUserDetails user) : this(user.Domain, user.UserName, user.Password)
        { }
        public Impersonation(string domain, string username, string password)
        {
            var ok = LogonUser(username, domain, password,
                           LOGON32_LOGON_INTERACTIVE, 0, out this._handle);
            if (!ok)
            {
                var errorCode = Marshal.GetLastWin32Error();
                throw new ApplicationException(string.Format("Could not impersonate the elevated user.  LogonUser returned error code {0}.", errorCode));
            }

            this._context = WindowsIdentity.Impersonate(this._handle.DangerousGetHandle());
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                this._context.Dispose();
                this._handle.Dispose();
            }           
            disposed = true;
        }

        ~Impersonation()
        {
            Dispose(false);
        }


        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

        sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTokenHandle()
                : base(true) { }

            [DllImport("kernel32.dll")]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            [SuppressUnmanagedCodeSecurity]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr handle);

            protected override bool ReleaseHandle()
            {
                return CloseHandle(handle);
            }
        }
    }

    public class ImpersonateUserDetails
    {
        public string UserName { get; set; }

        public string Password { get; set; }

        public string Domain { get; set; }
    }

One big catch here is that, you must have to use "LOGON32_LOGON_INTERACTIVE" as a "dwLogonType" param value while calling "LogonUser()" method. if you use something else (like LOGON32_LOGON_NEW_CREDENTIALS = 9) you will not get any error but printing won't work. Code will send the document to print queue with owner name as your machine name, file name as printdocument and size as 0 bytes. Overall impersonation is unsuccessful hence printing will fail.




using (new Impersonation("domain", "username", "password"))
{
    string filePath = @"c:\temp\printfile.pdf";
    string fileName = "printfile.pdf";
    \\this should be the fully qualified name of the printer.
    string printerName = @"\\server_name\printer_name"; 
    SendPdfToPrinter(content, docName, printerFullName);
}

Issue #3: Impersonation issue for 32 bit application.
In case if your application is hosted as 32 bit application then you will receive an Win32Exception saying “The handle is not valid." for impersonating the user.This is a well known issue of Microsoft for impersonation and they have patches releases for that as well. For more information refer this:
Printing successfully using impersonation from a 32-bit application-on-a-64-bit-system/

And if your application is using 64-bit application setup then no problem, you are ready to go.