Search code examples
c#printingwmipostscript

Document scaled when printing raw data


I'm trying to print pdf file directly on printer, which supports postscript. Unfortunately printed document is scaled (little smaller than print from adobe reader, with scaling mode - "Actual size"). Is there any way to print it without scaling? Here is a code, which I'm using to print:

  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, ThrowOnUnmappableChar = true, BestFitMapping = false)]
    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter(IntPtr hPrinter);

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, ThrowOnUnmappableChar = true, BestFitMapping = false)]
    public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndDocPrinter(IntPtr hPrinter);

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartPagePrinter(IntPtr hPrinter);

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndPagePrinter(IntPtr hPrinter);

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), 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 static bool SendFileToPrinter(string pdfFileName, string printerName, string name)
    {
        try
        {
            bool success = false;
            using (FileStream fs = new FileStream(pdfFileName, FileMode.Open))
            {
                int nLength = Convert.ToInt32(fs.Length);

                BinaryReader br = new BinaryReader(fs);

                Byte[] bytes = new Byte[fs.Length];
                bytes = br.ReadBytes(nLength);

                IntPtr ptrUnmanagedBytes = new IntPtr(0);
                ptrUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);

                Marshal.Copy(bytes, 0, ptrUnmanagedBytes, nLength);

                success = SendBytesToPrinter(printerName, ptrUnmanagedBytes, nLength, name);

                Marshal.FreeCoTaskMem(ptrUnmanagedBytes);
            }
            return success;
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }


    private static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount, string name)
    {
        try
        {
            Int32 dwError = 0, dwWritten = 0;
            IntPtr hPrinter = new IntPtr(0);
            DOCINFOA di = new DOCINFOA();
            bool success = false; // Assume failure unless you specifically succeed.

            di.pDocName = name;
            di.pDataType = "RAW";

            if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
            {
                if (StartDocPrinter(hPrinter, 1, di))
                {
                    if (StartPagePrinter(hPrinter))
                    {
                        success = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                        EndPagePrinter(hPrinter);
                    }
                    EndDocPrinter(hPrinter);
                }
                ClosePrinter(hPrinter);
            }

            if (success == false)
            {
                dwError = Marshal.GetLastWin32Error();
            }
            return success;
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }

By the way - can I manage printer margins, when I'm printing like that?

Thanks, Bartosz


Solution

  • Thanks for advice @KenS, finally I've found a solution why PDF's not printing properly. There was a problem with default PDF scaling, which is trying to fit print to page. Solution for this is to set scaling:

    /ViewerPreferences<</PrintScaling/None>> 
    

    in PDF file, for example:

    object before:

    2 0 obj
    <<
    /Type/Catalog
    /Pages 3
    >>
    endobj
    

    object after:

    2 0 obj
    <<
    /Type/Catalog
    /ViewerPreferences<</PrintScaling/None>>
    /Pages 3
    >>
    endobj