Чтение контекста устройства принтера на языке C#

#c# #winapi #printing #bitmap #gdi

#c# #winapi #печать #растровое изображение #gdi

Вопрос:

Я могу отображать и сохранять экранное устройство в DIB bitmap, и я хотел бы сделать то же самое с контекстом устройства принтера. Я могу получить ненулевое растровое изображение, но оно всегда сплошное черное.

Вот код, который позволяет мне обрабатывать контекст экранного устройства.

         //In size variable we shall keep the size of the window.
        SIZE size;

        //Win32 API functions are imported in classes
        //PlatformInvokeGDI32
        //PlatformInvokeUSER32

        //Get handle of calc.exe window.
        IntPtr hwnd = PlatformInvokeUSER32.FindWindow("SciCalc", "Calculator");

        //Get window dimensions
        PlatformInvokeUSER32.RECT rect;
        PlatformInvokeUSER32.GetWindowRect(hwnd, out rect);
        size.cx = rect._Right - rect._Left;
        size.cy = rect._Bottom - rect._Top;

        //Get the device context of Calculator.
        IntPtr hDC = PlatformInvokeUSER32.GetDC(hwnd);

        //Draw on the Calculator surface.
        Graphics CalculatorGraphics = Graphics.FromHdc(hDC);
        Color colorRed = Color.FromName("Red");
        Pen penRed = new Pen(colorRed);
        CalculatorGraphics.DrawEllipse(penRed, 50, 50, 50, 50);
        CalculatorGraphics.Save();

        //Here we make a compatible device context in memory for screen device context.
        IntPtr hMemDC = PlatformInvokeGDI32.CreateCompatibleDC(hDC);

        //Create a compatible bitmap of window size and using screen device context.
        m_HBitmap = PlatformInvokeGDI32.CreateCompatibleBitmap(hDC, size.cx, size.cy);

        //As m_HBitmap is IntPtr we can not check it against null. For this purspose IntPtr.Zero is used.
        if (m_HBitmap != IntPtr.Zero)
        {
            //Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
            IntPtr hOld = (IntPtr)PlatformInvokeGDI32.SelectObject(hMemDC, m_HBitmap);
            //We copy the Bitmap to the memory device context.
            PlatformInvokeGDI32.BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, PlatformInvokeGDI32.SRCCOPY);
            //We select the old bitmap back to the memory device context.
            PlatformInvokeGDI32.SelectObject(hMemDC, hOld);
            //We delete the memory device context.
            PlatformInvokeGDI32.DeleteDC(hMemDC);
            //We release the screen device context.
            PlatformInvokeUSER32.ReleaseDC(hwnd, hDC);
            //Image is created by Image bitmap handle and returned.
            return System.Drawing.Image.FromHbitmap(m_HBitmap);
        }
        //If m_HBitmap is null retunrn null.
        return null;
  

Вот как я получаю контекст устройства принтера:

         //In size variable we shall keep the size of the window.
        SIZE size;
        size.cx = 1000;
        size.cy = 1000;

        //Get Printer Handle
        IntPtr PrinterHandle;
        PlatformInvokeGDI32.PRINTER_DEFAULTSW defaults = new PlatformInvokeGDI32.PRINTER_DEFAULTSW();
        PlatformInvokeGDI32.OpenPrinterW("Bullzip PDF Printer", out PrinterHandle, defaults);

        //Get Printer Device context
        IntPtr PrinterHDC = PlatformInvokeGDI32.CreateDCW("", "Bullzip PDF Printer", "", IntPtr.Zero);

        //Initialize DocInfo structure.
        PlatformInvokeGDI32.DOCINFOW docInfo = new PlatformInvokeGDI32.DOCINFOW();

        //Start printing.
        PlatformInvokeGDI32.StartDocW(PrinterHDC, ref docInfo);
        PlatformInvokeGDI32.StartPage(PrinterHDC);
        Graphics graphics = Graphics.FromHdc(PrinterHDC, PrinterHandle);

        Color theColor = Color.FromName("Red");
        Pen pen = new Pen(theColor);
        graphics.DrawRectangle(pen, 50, 50, 50, 50);
        graphics.DrawEllipse(pen, 50, 50, 50, 50);
        graphics.Save();

        //Here we make a compatible device context in memory for screen device context.
        IntPtr hMemDC = PlatformInvokeGDI32.CreateCompatibleDC(PrinterHDC);

        //Create a compatible bitmap of window size and using screen device context.
        m_HBitmap = PlatformInvokeGDI32.CreateCompatibleBitmap(PrinterHDC, size.cx, size.cy);

        //As m_HBitmap is IntPtr we can not check it against null. For this purspose IntPtr.Zero is used.
        if (m_HBitmap != IntPtr.Zero)
        {
            //Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
            IntPtr hOld = (IntPtr)PlatformInvokeGDI32.SelectObject(hMemDC, m_HBitmap);
            //We copy the Bitmap to the memory device context.
            PlatformInvokeGDI32.BitBlt(hMemDC, 0, 0, size.cx, size.cy, PrinterHDC, 0, 0, PlatformInvokeGDI32.SRCCOPY);
            //We select the old bitmap back to the memory device context.
            PlatformInvokeGDI32.SelectObject(hMemDC, hOld);
            //We delete the memory device context.
            PlatformInvokeGDI32.DeleteDC(hMemDC);
            //We release the screen device context.
            //PlatformInvokeUSER32.ReleaseDC(hwnd, PrinterHDC);
            //Image is created by Image bitmap handle and returned.

            PlatformInvokeGDI32.EndPage(PrinterHDC);
            PlatformInvokeGDI32.EndDoc(PrinterHDC);
            PlatformInvokeGDI32.ClosePrinter(PrinterHandle);
            pen.Dispose();
            graphics.Dispose();


            return System.Drawing.Image.FromHbitmap(m_HBitmap);
        }
        //If m_HBitmap is null retunrn null.
        return null;
  

Как я могу использовать контекст устройства принтера?
Как я могу сохранить контекст устройства принтера в DIB?

Спасибо,

Джейкоб

Комментарии:

1. Это очень необычный поступок. Поддельный драйвер принтера с именем типа «Bullzip PDF Printer», без сомнения, не поддерживает его. Работает как растровое устройство, а не как растровое.

2. Теперь я попробовал тот же код с другими физическими и программными принтерами и получил те же результаты: графика печатается успешно, но DC не преобразуется в растровое изображение, как я надеялся. Принтеры: HP LaserJet 4100 Series PCL, Apple Color LaserWriter 12/600, принтер Bullzip PDF, DOXPrinter801 и LaserWriter Personal NT v51.8. Принтер Ricoh Aficio MP C4000 PCL6 выдал исключение AccessViolationException в PlatformInvokeGDI32.StartPage(PrinterHDC);.

Ответ №1:

Контексты устройств принтера доступны только для записи.

Например, предположим, что вы печатаете на принтере PostScript. Вы создаете контекст устройства принтера и рисуете на нем некоторый текст. Драйвер устройства принтера не отображает растровое изображение, содержащее ваш текст. Скорее, он создает последовательность команд PostScript для рисования текста и отправляет команды на принтер. Другими словами, нет растрового изображения для копирования.

Чего вы пытаетесь достичь?

Комментарии:

1. Я печатаю большие объемы документов в TIFF программно. Иногда документы представляют собой электронные письма на основе HTML, содержащие таблицы, слишком широкие для печати на странице, что означает, что часть содержимого не печатается. Я надеялся сохранить растровое изображение, а затем определить, есть ли какие-либо небелые пиксели за пределами области, которые можно печатать на странице.

2. Не могли бы вы загрузить электронное письмо в средство визуализации HTML и запросить у него ширину документа?

3. Электронные письма хранятся в PST Outlook. Приложение, которое отправляет электронные письма на печать, представляет собой готовое приложение под названием DiscoveryCracker, и оно отправляет электронные письма в Outlook 2007 для печати. Я не знаю, где в процессе я мог бы отправить документ в средство визуализации HTML. Причина, по которой я хотел использовать контекст устройства, заключается в том, что я могу внедрить DLL в DiscoveryCracker или Outlook и подключить StartPage, который использует hDC.

4. Я не специалист по Outlook, но, похоже, можно написать макрос для запуска при загрузке сообщения. Однако, если печатается только подмножество открываемых сообщений, от этого будет мало толку. В отличие от Excel, кажется невозможным запустить макрос при печати документа. В качестве альтернативы, если вы подключаете GDI, вы можете подключить функции рисования текста и линий и проверить их границы. Хотя это может усложниться, если вам нужно учитывать режим отображения. Или же может существовать более чистый способ подключения к конвейеру печати.

5. Получение координат ExtTextOut и других текстовых методов — это другой способ, о котором я подумал. Я согласен с тем, что необходимость учитывать режим отображения усложнит задачу. Если есть более чистый способ подключиться к конвейеру печати, я его не нашел. У вас есть какие-нибудь идеи? Идея макроса — это не то, о чем я думал. Я подумаю над этим.