Печать на принтере

Для того, чтобы осуществлять графический вывод на принтер, необходимо создать объект Graphics, связанный с графическим контекстом принтера. Кроме того, необходимо создать документ принтера, на страницах которого мы будем рисовать.

Создание графического контекста принтера

Один из вариантов создания графического контекста принтера для работы с ним из GDIPlus заключается в применении Windows API функции CreateDC. Эта функция получает имя принтера, создаёт необходимую структуру и возвращает указатель на неё:

 
 DECLARE Long CreateDC IN Gdi32.dll String lpszDriver, String lpszDevice, ;
         String lpszOutput, String Devmode
 hDC = CreateDC(NULL, Printer_Name, NULL, NULL)
 

Функция получает следующие параметры:

  • lpszDriver — указатель на нуль-терминированную строку, содержащую имя драйвера. Должен быть NULL, если указан параметр lpszDevice

  • lpszDevice — указатель на нуль-терминированную строку, содержащую имя принтера.

  • lpszOutput — не используется; должен быть NULL

  • Devmode — указатель на структуру DEVMODE, содержащую описание присущих устройству параметров, используемых при инициализации драйвера устройства. Эта структура позволяет управлять положением листа, качеством графики и рядом других параметров; вы можете найти её описание в MSDN.

Если функция CreateDC возвращает ноль, то это означает, что графический контекст не создан.

Для получения имени принтера можно воспользоваться встроенной функцией GETPRINTER(). Эта функция формирует диалоговое окно (рис.23.1), в котором вы можете выбрать как локальный принтер, так и любой другой из доступных в вашей локальной сети, и возвращает строку с именем выбранного принтера:

 
 Printer_Name = GETPRINTER()
 

Рис. 23.1. Диалог для выбора принтера

Если вы хотите печатать на принтере, установленном по умолчанию, то воспользуйтесь функцией
SET("Printer"):

Printer_Name = SET("Printer", nType)

где параметр nType может принимать одно из следующих значений:

2 — принтер по умолчанию в Windows
3 — принтер по умолчанию в Visual FoxPro

Так как Visual FoxPro «не видит» память, зарезервированную функциями Windows API, то вы должны самостоятельно удалить графический контекст принтера, вызвав функцию DeleteDC:

 
 DECLARE Long DeleteDC IN Gdi32.dll Long hDC
 = DeleteDC(hDC)
 

Создание связанного с принтером объекта Graphics

Для создания требуемой модификации объекта Graphics для вывода на принтер используется функция GdipCreateFromHDC. Вот её объявление в Visual FoxPro:

 
 DECLARE Long GdipCreateFromHDC IN Gdiplus.dll Long hdc, Long @ nativeGraphics
 

Функции передаётся два параметра. Параметр hdc — это указатель на структуру, содержащую графический контекста принтера, а в передаваемом по ссылке параметре nativeGraphics запоминается дескриптор созданного объекта Graphics.
В следующем фрагменте кода показано, как создать объект Graphics, связанный с принтером, используемым по умолчанию:

 
 cPrinterName = SET("Printer", 2)
 hDC = CreateDC(NULL, cPrinterName, NULL, NULL)
 IF hDC != 0
    lnGraphics = 0
    Status = GdipCreateFromHDC(hDC, @lnGraphics)
 ENDIF
 

Определение размера листа принтера

Справедливо предположить, что для рисования на принтере вы захотите использовать систему координат, единицей измерения в которой являются миллиметры. Поэтому не плохо было бы узнать, страницу какого размера ваш принтер может напечатать. Для определения размеров страницы используется функция GetDeviceCaps. Вот её объявление:

 
 DECLARE Long GetDeviceCaps IN Gdi32.dll Long hdc, Long nIndex
 

Параметр hdc — это указатель на структуру, содержащую графический контекст принтера, а nIndex — число, принимающее одно из значений, перечисленных в таблице 23.1.

Таблица 23.1. Значения параметра nIndex функции GetDeviceCaps

nIndex Значение
4  Ширина листа в миллиметрах
6  Высота листа в миллиметрах
8  Ширина листа в пикселях
10  Высота листа в пикселях

В следующем фрагменте кода показано, как определить размеры листа (в мм) для принтера, установленного по умолчанию:

 
 CLEAR 
 DECLARE Long CreateDC IN Gdi32.dll String, String, String, String
 DECLARE Long GetDeviceCaps IN Gdi32.dll Long, Long
 hDC = CreateDC(NULL, SET("Printer", 2), NULL, NULL)
 ? "Ширина", GetDeviceCaps(hDC, 4)
 ? "Высота", GetDeviceCaps(hDC, 6)
 

Напомню, что установить единицу измерения для печати на принтере можно функцией GdipSetPageUnit, описание которой приведено в начале предыдущей главы.

Документ принтера

При печати изображений на принтере первоначально весь графический вывод направляется в документ принтера. Документ может содержать произвольное количество страниц. Каждую страницу документа условно можно рассматривать как аналог растра с размерами, равными размерам листа бумаги. После того, как все необходимые страницы документа сформированы, документ закрывается и направляется в очередь печати.

Создание документа принтера

Создаёт документ принтера функция StartDoc. Эта функция получает два параметра: указатель на графический контекст принтера и указатель на структуру DOCINFO. В общем случае структура DOCINFO может быть представлена символьной строкой длиной 20 байт, первый байт которой имеет значение 20.
В следующем фрагменте кода показан вариант создания документа принтера с использованием пустой структуры DOCINFO.

 
 DECLARE Long StartDoc IN Gdi32.dll Long hdc, String Docinfo
 cDocInfo = CHR(20) + REPLICATE(CHR(0), 19)      && Формируем структуру
 Result = StartDoc(hDC, cDocInfo)                && Создаём документ
 

Если документ создан успешно, то функция возвращает значение, отличное от нуля, а в случае ошибки — ноль.

Заполнение структуры DOCINFO

Эта структура подробно рассматривалась в главе 19, в разделе «Распределение памяти для структур с указателями». Напомню, что она содержит пять четырёхбайтовых полей, из которых интерес представляет второе поле, которое содержит указатель на строку с именем печатаемого документа. Это имя выводится в окне просмотра очереди печати (рис. 23.2) в колонке «Документ».

Рис. 23.2. Окно просмотра очереди печати

Следующий фрагмент кода показывает, как документу принтера присвоить имя. Предполагается, что это имя хранится в переменной cDocName.

 
 nLen = LEN(cDocName)
 * Распределяем глобальную память для размещения строки с именем документа
 hGlobal = GlobalAlloc(0x0040, nLen)
 * Копируем строку в эту память
 = SYS(2600, hGlobal, nLen, cDocName)
 * Формируем структуру DOCINFO
 cDOCINFO = BINTOC(20,"4RS") + BINTOC(hGlobal,"4RS") + ;
 REPLICATE(CHR(0),12)
 

Не забудьте освободить распределённую функцией GlobalAlloc память после закрытия документа принтера!

Управление страницами документа

Функция StartPage добавляет в документ новую страницу и делает её активной. Вы можете рисовать только на активной странице. Функция получает только один параметр — указатель на графический контекст принтера.
Закрывает страницу функция EndPage. Закрытую страницу невозможно вновь сделать активной. Функция так же получает только один параметр — указатель на графический контекст принтера.
После того, как вы закрыли страницу, вы можете открыть следующую страницу и продолжить рисовать уже на ней.
Следующий фрагмент кода показывает, как организуется работа со страницами документа принтера.

 
 DECLARE Long StartPage IN Gdi32.dll Long hDC
 DECLARE Long EndPage IN Gdi32.dll Long hDC
 * Предполагаем, что документ уже создан
 Result = StartPage(hDC)
 * Здесь – вызов функций GDIPlus для рисования на странице
 Result = EndPage(hDC)
 * Создаём следующую страницу документа принтера
 Result = StartPage(hDC)
 * и так далее
 

Функции StartPage и EndPage при успешном выполнении возвращают отличное от нуля значение и ноль в случае ошибки.

Закрытие документа

Для того, чтобы послать документ принтера в очередь печати, необходимо его закрыть. Делается это при помощи функции EndDoc. Функция получает только один параметр — указатель на графический контекст принтера:

 
 DECLARE Long EndDoc IN Gdi32.dll Long hDC
 Result = EndDoc(hDC)
 

Класс для печати на принтере

Создадим из функций, реализующих печать на принтере, класс с именем GdipPrinter. Добавьте этот класс в библиотеку VfpGdiPlus.vcx (мы по-прежнему продолжаем создавать классы для работы с GDIPlus!), в качестве класса-родителя выберите Custom.

Добавьте в класс следующие защищённые свойства:

  • Status — для хранения состояния класса
  • hDC — для хранения указателя на графический контекст принтера
  • nativeGraphics — для хранения дескриптора объекта Ghaphics
  • HGLOBAL — для хранения указателя на распределённую для наименования документа память
  • DocStatus — для хранения статуса документа принтера («истина», если документ существует)

Начальные значения всех этих свойств, кроме свойства DocStatus, установите равными нулю.

Метод Init

Поскольку класс GdipPrinter задуман как расширение класса GdipImages, то предполагается, что объект — экземпляр этого класса будет создаваться уже после того, как создан объект — экземпляр класса GdipImages. Таким образом, инициализация GDIPlus в этом методе не требуется.
В метод может быть передан один необязательный параметр: имя принтера, на котором вы хотите печатать. Если имя принтера опущено, то графический контекст будет создан для принтера, используемого на вашем компьютере по умолчанию.
Код метода показан в листинге 23.1.

Листинг 23.1. Метод Init класса GdipPrinter

 
 LPARAMETERS tcPinterName
 LOCAL lcPrinter, lnGraphics, lhDC
 IF VARTYPE(tcPrinterName) = "C"      && Если имя принтера передано методу 
    lcPrinter = tcPinterName          && то используем его
 ELSE                                 && иначе - 
    lcPrinter = SET("Printer", 2)     && берём принтер по умолчанию
 ENDIF 
 IF LEN(ALLTRIM(lcPrinter))
    DECLARE Long CreateDC IN Gdi32.dll String, String, String, String
    DECLARE Long GetDeviceCaps IN Gdi32.dll Long, Long
    lhDC = CreateDC(NULL, lcPrinter, NULL, NULL)
    IF lhDC != 0
       this.hDC = lhDC
       lnGraphics = 0
       DECLARE Long GdipCreateFromHDC IN Gdiplus.dll Long, Long @
       this.Status = GdipCreateFromHDC(lhDC, @lnGraphics)
       IF this.Status = 0
          this.nativeGraphics = lnGraphics
          RETURN .t.                  && Объект создан успешно
       ENDIF                          && иначе - 
       DECLARE Long DeleteDC IN Gdi32.dll Long
       = DeleteDC(lhDC)               && удаляем графический контекст принтера
    ENDIF 
 ENDIF 
 RETURN .f.
 

Переменная lcPrinter получает либо переданное в метод имя принтера, либо имя принтера, используемого на компьютере по умолчанию. Функция CreateDC создаёт графический контекст принтера и запоминает указатель на него в свойстве hDC. Далее создаётся связанный с принтером объект Graphics; дескриптор этого объекта сохраняется в свойстве nativeGraphics.
Функция DeleteDC удаляет графический контекст принтера в случае, если не удалось создать связанный с ним объект Graphics; в этом случае, а так же в случае, если невозможно обнаружить принтер, объект — экземпляр класса GdipPrinter не создаётся.
В следующем фрагменте кода демонстрируется создание объектов — экземпляров класса GdipPrinter:

 
 * Подключение к произвольному принтеру в вашей локальной сети
 SET CLASSLIB TO vfpgdiplus.vcx
 oPrint = CREATEOBJECT("GdipPrinter", GETPRINTER())
 * Подключение к принтеру, используемому по умолчанию.
 SET CLASSLIB TO vfpgdiplus.vcx
 oPrint = CREATEOBJECT("GdipPrinter")
 

Метод GetGraphics

Этот метод возвращает дескриптор объекта Graphics, связанный с принтером. Дескриптор может использоваться функциями рисования, рассмотренными в предыдущей главе.
Метод содержит только одну команду:

 
 RETURN this.nativeGraphics
 

Установка единицы измерения. Метод SetPageUnit

Вы можете указывать различные единицы измерения для каждой страницы документа принтера. Метод SetPageUnit получает три параметра, первый из которых (логического типа) определяет, какая единица измерения будет использоваться при рисовании на странице документа принтера (значение «ложь» определяет пиксели, значение «истина» — миллиметры), а в остальные передаваемые по ссылке параметры будет записано значение ширины и высоты листа в выбранной единице измерения. Код метода приведён в листинге 23.2.

Листинг 23.2. Код метода SetPageUnit класса GdipPrinter

 
 LPARAMETERS tlUnit, tnListWidth, tnListHeight
 LOCAL lnUnit
 IF VARTYPE(tlUnit) + VARTYPE(tnListWidth) + VARTYPE(tnListHeight) = "LNN"
    DECLARE Long GetDeviceCaps IN Gdi32.dll Long, Long
    IF tlUnit && Выбрано: миллиметры
       lnUnit = 6
       tnListWidth = GetDeviceCaps(this.hDC, 4)
       tnListHeight = GetDeviceCaps(this.hDC, 6)
    ELSE && Выбрано: мировые координаты
       lnUnit = 3
       tnListWidth = GetDeviceCaps(this.hDC, 8)
       tnListHeight = GetDeviceCaps(this.hDC, 10)
    ENDIF 
    DECLARE Long GdipSetPageUnit IN Gdiplus.dll Long, Long
    this.Status = GdipSetPageUnit(this.nativeGraphics, lnUnit)
 ELSE 
    this.Status = 2
 ENDIF 
 RETURN this.Status = 0
 

В следующем фрагменте кода показано, как использовать метод SetPageUnit для установки единицы измерения «миллиметры»:

 
 nPageWidth = 0
 nPageHeight = 0
 oPrint.SetPageUnit(.t., @nPageWidth, @nPageHeight)
 

После выполнения этого кода переменные nPageWidth и nPageHeight будут содержать значения ширины и высоты страницы принтера в миллиметрах.

Создание документа принтера. Метод OpenDocument

Создаёт документ принтера метод OpenDocument. Он получает только один параметр — имя печатаемого документа.
Если документ создан успешно, то в нём сразу открывается страница, на которой вы можете рисовать при помощи объекта Ghaphics. Код метода приведён в листинге 23.3.

Листинг 23.3. Код метода OpenDocument класса GdipPrinter

 
 LPARAMETERS tcDocumentName
 LOCAL lcDOCINFO, lnLen
 IF VARTYPE(tcDocumentName) = "C"
 * Имя документа передано. Формируем структуру DOCINFO
    tcDocumentName = ALLTRIM(tcDocumentName)
    lnLen = LEN(tcDocumentName)
    IF lnLen > 0
       this.hGlobal = GlobalAlloc(0x0040, lnLen)
       = SYS(2600, this.hGlobal, lnLen, tcDocumentName)
    ENDIF 
 ENDIF 
 cDOCINFO = BINTOC(20,"4RS") + BINTOC(this.hGlobal,"4RS") + REPLICATE(CHR(0),12)
 DECLARE Long StartDoc IN Gdi32.dll Long, String
 IF StartDoc(this.hDC, lcDOCINFO) > 0         && Создаём документ
    this.DocStatus = .t.
    DECLARE Long StartPage IN Gdi32.dll Long
    IF StartPage(this.hDC) > 0                && Открываем страницу
       RETURN .t.
    ENDIF 
 ENDIF 
 RETURN .f.
 

Метод NewPage

Метод закрывает текущую страницу и открывает новую. Его код показан в листинге 23.4.

Листинг 23.4. Код метода NewPage класса GdipPrinter

 
 this.Status = IIF(this.DocStatus, 0, -1)
 IF this.Status = 0
    DECLARE Long StartPage IN Gdi32.dll Long
    DECLARE Long EndPage IN Gdi32.dll Long
    IF EndPage(this.hDC) > 0
       IF StartPage(this.hDC) > 0
          RETURN .t.
       ELSE 
          this.Status = -2 && Невозможно открыть / закрыть страницу
       ENDIF 
    ELSE 
       this.Status = -2
    ENDIF 
 ENDIF 
 RETURN .f.
 

После вызова этого метода вы уже не сможете вернуться на предыдущие страницы документа для правки.

Метод CloseDocument

Метод закрывает страницу документа принтера и сам документ и направляет его в очередь печати. Код метода приведён в листинге 22.5.

Листинг 22.5. Метод CloseDocument класса GdipPrinter

 
 this.Status = IIF(this.DocStatus, 0, -1)
 IF this.Status = 0
    DECLARE Long EndPage IN Gdi32.dll Long
    DECLARE Long EndDoc IN Gdi32.dll Long
    = EndPage(this.hDC)
    IF EndDoc(this.hdc) > 0
       this.DocStatus = .f.
       RETURN .t.
    ELSE 
       this.Status = -3
    ENDIF
 ENDIF 
 RETURN .f.
 

Метод проверяет, был ли создан документ принтера, и, если да, закрывает страницу и сам документ, после чего устанавливает свойство DocStatus класса в «ложь».

Метод GetStatus

Как и в классе GdipImages, назначение этого метода — вернуть значение свойства Status, содержащего коды завершения функций GDIPlus. Он содержит только одну команду:

 
 RETURN this.Status
 

Метод Destroy

Так как этот метод вызывается при уничтожении объекта, мы должны включить в него команды для освобождения памяти, занимаемой графическим контекстом принтера, объектом Graphics и, возможно, строкой — наименованием документа. Кроме того, если документ принтера ещё не закрыт, то в методе он закрывается и отправляется в очередь печати.

Листинг 23.6. Код метода Destroy класса GdipPrint

 
 IF this.DocStatus 
    this.CloseDocument() 
 ENDIF 
 DECLARE Long DeleteDC IN Gdi32.dll Long
 DECLARE Long GdipDeleteGraphics IN Gdiplus.dll Long
 = GdipDeleteGraphics(this.nativeGraphics)
 = DeleteDC(this.hDC)
 IF this.hglobal != 0
    DECLARE Long GlobalFree IN WIN32API Long
    = GlobalFree(this.hGlobal)
 ENDIF

Тестирование

Тестовые примеры, приведённые ниже, демонстрируют способы печати на принтере графических примитивов и изображений.

Печатаем графические примитивы и тексты

Создайте в проекте новый программный файл с именем test15.prg и введите в него код из листинга 23.7.

Листинг 23.7. Test15.prg. Тестирование печати графических примитивов и текста

 
 LOCAL lcPath, loGP, llStatus, lcText, lnWidth, lnHeight, lnLeft, lnGraphics
 lcPath = JUSTPATH(SYS(16))
 SET DEFAULT TO (lcPath)
 SET CLASSLIB TO vfpgdiplus
 loGP = CREATEOBJECT("gdipimages")
 loPrint = CREATEOBJECT("gdipPrinter")
 IF VARTYPE(loPrint) != "O"
    = MESSAGEBOX("Не удалось подключиться к принтеру"
    RETURN
 ENDIF 
 * Получаем дескриптор объекта Graphics, связанного с принтером
 lnGraphics = loPrint.GetGraphics()
 * Открываем документ принтера
 llStatus = loPrint.OpenDocument()
 IF llStatus
 * Создаём перо
    llStatus = loGP.CreatePen(1)
 ENDIF 
 IF llStatus
* Рисуем пять прямоугольников, чтобы отметить области, в которые выводится текст
    llStatus = loGP.DrawRectangle(10, 40, 300, 60, lnGraphics)
    llStatus = loGP.DrawRectangle(10, 110, 300, 60, lnGraphics)
    llStatus = loGP.DrawRectangle(10, 180, 300, 60, lnGraphics)
    llStatus = loGP.DrawRectangle(320, 40, 110, 180, lnGraphics)
    llStatus = loGP.DrawRectangle(440, 40, 80, 180, lnGraphics)
    llStatus = loGP.DrawRectangle(530, 40, 100, 180, lnGraphics)
 ENDIF 
 IF llStatus
* Создаём одноцветную кисть коричневого цвета
    llStatus = loGP.CreateSolidBrush(0xFF882000)
 ENDIF 
 IF llStatus
 * Создаём шрифт высотой 16 пикселей
    llStatus = loGP.CreateFont("Times New Roman", 16)
 ENDIF 
 IF llStatus 
 * Центирование строки посредством вычисления её размеров
    lcText = "Эта строка центрируется по горизонтали"
    lnWidth = 0
    lnHeight = 0
    llStatus = loGP.GetLengthString(lcText, @lnWidth, @lnHeight, lnGraphics)
    lnLeft = (650 - lnWidth) / 2       && Определение левой точки
 ENDIF 
 IF llStatus
 * Рисование строки
    llStatus = loGP.DrawString(lnLeft, 5, 0, 0, lcText, lnGraphics)
 ENDIF 
 IF llStatus 
 * Создаём объект StringFormat 
    llStatus = loGP.CreateStringFormat()
 ENDIF 
 IF llStatus
    lcText = "Этот текст выводится в прямоугольную область без форматирования"
    llStatus = loGP.DrawString(10, 40, 300, 60, lcText, lnGraphics)
 ENDIF 
 IF llStatus
    lcText = "Строки этого текста центрируется внутри заданной прямоугольной области"
    llStatus = loGP.SetStringFormatParameter(1)
    llStatus = loGP.DrawString(10, 110, 300, 60, lcText, lnGraphics)
 ENDIF 
 IF llStatus
    lcText = "Этот текст выровнен по правому краю заданной прямоугольной области"
    llStatus = loGP.SetStringFormatParameter(2)
    llStatus = loGP.DrawString(10, 180, 300, 60, lcText, lnGraphics)
 ENDIF 
 IF llStatus
    lcText = "Этот текст выведен вертикально без форматирования " + ;
             "и коррекции качества начертания символов"
    llStatus = loGP.SetStringFormatParameter(0, .t.)
    llStatus = loGP.DrawString(320, 40, 110, 180, lcText, lnGraphics)
 ENDIF 
 IF llStatus
 * Улучшаем качество вертикально выводимого текста
    llStatus = loGP.SetTextRendering(4, lnGraphics)
 ENDIF 
 IF llStatus
    lcText = "Этот текст выведен вертикально и центрирован"
    llStatus = loGP.SetStringFormatParameter(1, .t.)
    llStatus = loGP.DrawString(440, 40, 80, 180, lcText, lnGraphics)
 ENDIF 
 IF llStatus
    lcText = "Этот текст выведен вертикально и прижат к нижней границе прямоугольной области"
    llStatus = loGP.SetStringFormatParameter(2, .t.)
    llStatus = loGP.DrawString(530, 40, 100, 180, lcText, lnGraphics)
 ENDIF 
 = MESSAGEBOX(IIF(llStatus, "ok","Ошибка" + STR(loGP.GetStatus())))
 

В начале тестового примера, после создания объекта — экземпляра класса GdipImages, создаётся объект — экземпляр класса GdipPrinter для принтера, используемого по умолчанию. Дескриптор объекта Graphics, связанный с принтером, запоминается в переменной lnGraphics и в дальнейшем используется всеми функциями рисования для вывода на принтер.
Запустите тест. Оцените качество печати (а ведь мы не устанавливали для принтера никаких единиц измерения; по умолчанию используется единица измерения UnitWorld). Обратите внимание, что вызов метода SetTextRendering не оказывает на качество печатаемого текста никакого влияния; действительно, он эффективен только при выводе на жидкокристаллические мониторы. Вставьте в код тестового примера команды вызова метода SetPageUnit и установите в качестве единицы измерения миллиметры. Измените значения координат и размеров графических примитивов так, чтобы они соответствовали этой единице измерения. Запустите тест на выполнение. Возьмите линейку и проверьте, действительно ли размеры прямоугольников соответствуют указанным в миллиметрах.

Печатаем изображения

Создайте в проекте ещё один программный файл с именем test16.prg и введите в него код из листинга 23.8.

Листинг 23.8. Test16.prg. Тестирование печати изображений

 
 LOCAL lcPath, loGP, loPrint, llStatus, lcFile, lnGraphics
 lcPath = JUSTPATH(SYS(16))
 SET DEFAULT TO (lcPath)
 SET CLASSLIB TO vfpgdiplus
 lcFile = GETFILE('JPG|GIF|BMP')
 IF EMPTY(lcFile)
    RETURN
 ENDIF 
 * Создаём объект - экземпляр класса GdipImages
 loGP = CREATEOBJECT("gdipimages")
 * Загружаем графический файл
 IF loGP.LoadFromFile(lcFile)
    loPrint = CREATEOBJECT("gdipPrinter")
    IF VARTYPE(loPrint) != "O"
       = MESSAGEBOX("Не удалось подключиться к принтеру")
       RETURN
    ENDIF
 ELSE 
    RETURN 
 ENDIF
 * Получаем дескриптор объекта Graphics, связанного с принтером
 lnGraphics = loPrint.GetGraphics()
 lnWidth = 0
 lnHeight = 0
 loPrint.OpenDocument("Проверка графического вывода")
 loGP.DrawImage(lnGraphics, 30, 30)
 

Обратите внимание на то, что документу при его создании присваивается имя. Запустите пример на выполнение и выберите любой файл, содержащий изображение, которое нужно распечатать на принтере.

Попробуйте вызвать метод DrawImage, указав ширину и высоту прямоугольной области. Изображение будет центрироваться внутри этой области, полностью вписываясь в неё.
Как и в случае с предыдущим тестом, выберите миллиметры как единицу измерения для страницы принтера и снова выполните тест. И, наконец, попробуйте создать несколько страниц принтера при помощи метода NewPage и что-нибудь нарисовать или напечатать на них.

На этом мы закончим изучение особенностей печати на принтере и перейдём к гораздо более интересному разделу — рисованию в окне формы.