4. Печать документов с помощью Java 1.2 API
В JDK версий 1.2 компания Sun существенно расширила набор классов для печати. Эти классы сосредоточенны в пакете java.awt.print.* . В этом же пакете находятся классы описывающие исключительные ситуации, возникающие при печати. Кроме того, для формирования изображения выводимого на печать, программист может использовать методы класса java.awt.Graphics2D более мощные и эффективные для формирования двумерной графики, чем методы класса Graphics .
При печати документа в Java 1.2 API используется подход сходный с Java 1.0 API. Для того, чтобы распечать документ нобходимо выполнить следующие шаги:
- Получить ссылку на системный принтер
- Сформировать изображение для печати
- Распечатать подготовленное изображение
В качестве объекта использующегося для связи с системным принтером используется объект типа java.awt.print.PrinterJob . Ссылку на объект экземпляр класса PrinterJob можно получить с помощью статического метода этого же класса public static PrinterJob getPrinterJob() . Такое действие может выглядеть следующим образом: В случае, если в операционной системе компьютера на котором запускается программа принтер по умолчанию не установлен, метод getPrinterJob() вернет значение null . В отличие от ранее рассмотреного примера печати, при получении ссылки на объект связаный с принтером диалоговое окно не появляется. Для получения соответствующего диалога необходимо явно вызвать метод класса PrinterJob : public boolean printDialog() .
Такое действие может выглядеть следующим образом: Этот метод возвращает булевское значение true в случае если пользователь нажал подтверждающую кнопку в окне диалога, а также в случае если работа ведется в неграфическом режиме и диалоговое окно не может быть выведено на экран. В Java 1.4 API существует аналогичный перегруженый метод, который позволяет выставлять начальные значения в настройках принтера. Необходимые настройки также можно передать позже при печати.
Изображение для печати в API 1.2 формируется несколько иначе, чем в предыдущих версиях Java. Для формирования изображения необходимо создать класс реализующий интерфейс java.awt.print.Printable . От этого интерфейса создаваемый класс наследует единственный абстрактный метод public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException . Переопределяя этот метод программист собственно и определяет, что именно будет печататься принтером. Данный метод получает от вызывающей его программы ссылку на графический контекст принтера, рисуя на котором мы определяем изображение, выводимое на принтер. Объект типа Graphics передаваемый методу print() в качестве аргумента также реализует интерфейс java.awt.print.PrinterGraphics и соответвенно метод описаный в этом интерфейсе public PrinterJob getPrinterJob() , который позволяет внутри метода print() получить ссылку на объект типа PrinterJob .
Помимо ссылки на графический контекст принтера метод print() также получает ссылку на объект типа java.awt.print.PageFormat , позволяющий получить информацию о размерах области печати (с учетом или без учета полей принтера) и ориентации бумаги, выбраной в настройках принтера.
Третий аргумент получаемый методом print() — номер страницы для печати. Отсчет страниц ведется от 0. В случае если запрашиваемая страница может быть сформирована методом print() он должен вернуть целочисленную константу Printable.PAGE_EXISTS , в противном случае, если метод print() не может сформировать указанную страницу, он должен вернуть значение Printable.NO_SUCH_PAGE .
Ниже приводится пример класса формирующего изображения страниц для печати. Этот класс позволит распечатать 2 страницы отобразив на них их номера (исходный текст класса PageImage.java): При формировании изображения большое значение имеют его размеры, если изображение не попадает в область печати, то принтер не выполняет задание! Причем поля изначально установленные для документа отличаются от размеров полей системного принтера.

Для определения области печати и орентации листа необходимо воспользоваться методами объекта типа PageFormat:
| public double getHeight() | возвращает полную высоту листа в пикселах из расчета 72 пиксела на дюйм |
| public double getWidth() | возвращает полную ширину листа в пикселах из расчета 72 пиксела на дюйм |
| public double getImageableHeight() | возвращает высоту области печати в пикселах из расчета 72px/in |
| public double getImageableWidth() | возвращает ширину области печати в пикселах из расчета 72px/in |
| public double getImageableX() | смещение по горизонтали левого верхнего угла обасти печати относительно левого верхнего угла листа |
| public double getImageableY() | смещение по вертикали левого верхнего угла обасти печати относительно левого верхнего угла листа |
| public int getOrientation() | возвращает ориентацию листа в виде целочисленной константы: PageFormat.PORTRAIT , PageFormat.LANDSCAPE , PageFormat.REVERSE_LANDSCAPE (начало координат находится в правом-верхнем углу листа, в книжной ориентации, положительное горизонтальное направление справа на лево, а положительное вертикальное направление сверху вниз) |
Также в классе PageFormat существуют методы для смены текущих настроек принтера, например смены ориентации листа и других настроек принтера при помощи вспомогательного класса java.awt.print.Paper .
После описания класса формирующего изображение для печати, необходимо связать его с принтером и отпечатать создаваемый в нем документ. Для связывания принтера с объектом типа Printable , формирующим изображение для печати, в классе PrinterJob существует метод public void setPrintable(Printable painter) , а для печати документа используется метод public void print() throws PrinterException того же класса. Таким образом для печати документа можно использовать следующий код:
Временная задержка в 1 сек использована на случай, если операционная система в которой выполняется программа не сразу поставит задание в очередь на печать. При работе этого кода никаких окон появляться не будет и программа реализованная в примере J2SimplePrinting.java просто распечатает на принтере две страницы, сформированные в классе PageImage . Часто при написании подобных программ класс осуществляющий печать документа реализует в себе интерфейс Printable и переопределяет унаследованый метод print() , при этом программа получается более компактной.
Для формирования изображения можно использовать не только методы класса Graphics , но и воспользоваться возможностями класса Graphics2D . Для этого при переопределении метода print() , унаследованного от интерфейса Printable , необходимо осуществить явное преобразование типов: При этом программист получает мощнейшие возможности по формированию графики для печати с помощью методов класса Graphics2D , например: В примере J2CoolPrinting.java были реализованы подобные действия. Результат работы этого примера представлен ниже:

Все вышеописанные примеры приложений иллюстрируют возможность Java 1.2 API осуществлять печать в неграфическом (консольном) режиме. При необходимости программист может вызвать стандартные диалоги операционной системы. Выше уже описывалось использования метода printDialog() класса PrinterJob , вызывающего диалоговое окно настроек принтера. Кроме этого метода, можно обратиться к методу public PageFormat pageDialog(PageFormat page) класса PrinterJob , открывающему диалог настроек свойств страницы.

6. Современные методы печати
Библиотека классов java.awt.print описаная выше достаточно удобна для печати как одностраничных, так и многостраничных документов, однако у нее имеется неочевидный при первом рассмотрении недостаток: внешний вид страниц необходимо формировать с помощью самостоятельно созданных классов и методов. Такой подход вполне допустим если приложение разрабатываемое программистом должно формировать бланки или отчеты заранее определенного вида, но такой подход становится неудобным в случае создания универсальной программы печати документов различных форматов, например текстовых файлов, HTML-страниц, PDF-документов и т. п.
Еще один на первый взгляд не заметный недостаток печати при помощи Java 1.2 API — оконные диалоги, возникающие при печати не являются истинно платформенно независимыми. В различных операционных системах диалоговые окна настроек принтера и настроек страницы будут иметь различный внешний вид.
К недостаткам вышеописанных классов можно также отнести отсутствие возможности обрабатывать события связанные с принтером, что могло бы быть полезным при печати длинных документов, когда важно проследить не прервалась ли работа принтера и не изменились ли его настройки, и не очень высокую производительность печати, так как принтер печатает подготовленное изображение, т. е. работает в графическом режиме, в то время, как у многих принтеров существуют текстовые режимы печати, существенно более производительные, чем графический режим.
Описаные выше проблемы на сегодняшний день решаются с помощью дополнительных классов и методов для печати появившихся в Java 1.4 API. Все дополнительные к Java 1.2 API классы для печати сгруппированы в пакете javax.print и его подпакетах.
Принципиальное отличие нового API заключается в том, что при печати учитывается медиа тип содержимого документа, задаваемый при помощи стандартных, определенных в RFC-документах 2045 и 2046, MIME-типов (Multipurpose Internet Mail Extensions Types). Существует достаточно много MIME-типов форматирования данных, "понимаемых" Java 1.4 API, например: "text/plain" — текстовые ASCII документы, "text/html" — документы формата HTML, "application/pdf" — документы формата PDF, "application/postscript" — документы формата PostScript, "image/gif" и "image/jpeg" — соответствующие графические форматы, и т. д. Помимо возможности печатать предварительно отформатированные документы, сохранилась также возможность печати документов формируемых в пользовательских Java-классах, реализующих интерфейс java.awt.print.Printable. Таким источникам данных для печати соответствует MIME-тип "application/x-java-jvm-local-objectref". Для обозначения данных такого типа в англоязычной литературе используют термин "service formatted print data", а для данных стандартных MIME-типов, извлекаемых из некоторого источника, где они уже находились в предварительно отформатированном виде используют термин "client formatted print data".
Практически для осуществления печати документа, с помощью Java 1.4 API, необходимо выполнить следующую последовательность действий:
- Получить список доступных системе принтеров
- Создать объект для дальнейшего взаимодействия с принтером
- Описать тип форматирования (MIME-тип) печатаемых данных
- Задать свойства печати (количество и формат листов и т. п.)
- Установить связь с источником данных
- Распечатать данные
6.2 Получение списка принтеров
В качестве объекта идентифицирующего системный принтер выступает объект реализующий интерфейс javax.print.PrintService . Получение ссылки на этот объект производится с помощью специальных "поисковых" методов класса PrintServiceLookup :
- public static PrintService lookupDefaultPrintService() — отыскивает принтер по умолчанию, в случае если такой принтер отсутствует, возвращает null
Пример кода, выполняющего описанное действие:
6.3 Создание объекта для взаимодействия с принтером
Для взаимодействия с принтером используется объект типа javax.print.DocPrintJob по применению схожий с объектом типа java.awt.print.PrinterJob , применявшемся в Java 1.2 API. Ссылку на этот объект получают с помощью метода public DocPrintJob createPrintJob() объекта реализующего интерфейс PrintService .
Пример кода, выполняющего описанное действие:
6.4 Описание типа форматирования печатаемых данных
Описание типа форматирования тех данных, которые позже будут отпечатаны принтером, осуществляется с помощью объекта типа javax.print.DocFlavor и его вложеных классов. Принципиально класс DocFlavor содержит информацию о MIME-типе печатаемых данных, кодировке, если это данные текстовые, и типе источника данных в виде полностью определенного имени Java-класса (например: java.io.FileInputStream ).
Источник данных может поставлять данные в разном виде, причем выделяют два основных вида представления данных:
- Client Formatted Print Data — предварительно сформированные данные
- Service Formatted Print Data — динамически формирующиеся данные
Под предварительно сформированными данными подразумеваются данные читаемые в виде потока из файла или по сети. При потоковом чтении данных различают байтовые и символьные потоки. Обработка байтовых потоков производится с помощью вложенных классов класса DocFlavor:
- javax.print.DocFlavor.BYTE_ARRAY — позволяет обрабатывать данные поступающие из байтового массива — источник byte[]
- javax.print.DocFlavor.INPUT_STREAM — позволяет обрабатывать данные поступающие из байтового потока ввода — источник java.io.InputStream
- javax.print.DocFlavor.URL — позволяет обрабатывать данные поступающие из сетевого источника байтовых данных — источник java.net.URL
Для обработки текстовых потоков используются аналогичные вложенные классы:
- javax.print.DocFlavor.CHAR_ARRAY — позволяет обрабатывать данные поступающие из символьного массива — источник char[]
- javax.print.DocFlavor.READER — позволяет обрабатывать данные поступающие из текстового потока чтения — источник java.io.Reader
- javax.print.DocFlavor.STRING — позволяет обрабатывать данные поступающие из текстовой строки — источник java.lang.String
Каждый из этих классов может обрабатывать данные популярных MIME-типов на работу с которыми он расчитан. При этом "Java" понимает не все типы форматирования данных, но многие популярные типы могут быть прочитаны этими классами и отпечатаны с учетом особенностей форматирования применявшихся в иточниках данных. В каждом из этих классов описаны статические константы определяющие какие MIME-типы данных может обрабатывать этот класс:
| Класс BYTE_ARRAY | |
| константа | комментарий |
|---|---|
| AUTOSENSE | соответствует MIME-типу "application/octet-stream", печатает данные "как есть", без разбора формата, что хорошо подходит для текстовых документов формата *.txt |
| GIF | соответствует MIME-типу "image/gif" |
| JPEG | соответствует "image/jpeg" |
| PNG | соответствует "image/png" |
| соответствует "application/pdf" | |
| POSTSCRIPT | соответствует "application/postscript" |
| TEXT_HTML_US_ASCII | соответствует "text/html;charset=us-ascii" |
| TEXT_PLAIN_US_ASCII | соответствует "text/plain;charset=us-ascii" |
| Класс INPUT_STREAM | |
| константы этого класса, такие же, как и в классе BYTE_ARRAY | |
| Класс URL | |
| константы этого класса, такие же, как и в классе BYTE_ARRAY | |
| Класс CHAR_ARRAY | |
| TEXT_HTML | соответствует "text/html;charset=utf-16" |
| TEXT_PLAIN | соответствует "text/plain;charset=utf-16" |
| Класс READER | |
| константы этого класса, такие же, как и в классе CHAR_ARRAY | |
| Класс STRING | |
| константы этого класса, такие же, как и в классе CHAR_ARRAY | |
В качестве динамически формирующихся данных выступают данные для печати формирующиеся методами применявшимися для печати в Java 1.2 API. Для их описания применяется еще один вложенный класс класса DocFlavor:
- javax.print.DocFlavor.SERVICE_FORMATTED
В этом классе также определены статические константы, описывающие разные способы формирования документа для печати:
- PAGEABLE — соответствует данным представленным в виде объекта, реализующего интерфейс java.awt.print.Printable
- PRINTABLE — соответствует данным представленным в виде объекта, реализующего интерфейс java.awt.print.Pageable (например класс Book)
- RENDERABLE_IMAGE — соответствует данным представленным в виде объекта, реализующего интерфейс java.awt.image.renderable.RenderableImage
Пример кода указания формата и источника данных: Прежде чем указывать формат и источник данных, желательно убедиться, что такие форматы и источники поддерживаются установленными у вас Java-библиотеками, так как не все медиа-типы из описаных выше имеют реальную поддержку. Для этого можно воспользоваться например таким кодом, проверющим возможность работы с изрображениями формата JPEG, считываемыми из потока ввода: А чтобы увидеть список всех поддерживаемых в вашей системе форматов, можно использовать следующий код:
6.5 Задание свойств печати
Все вышеописанные классы позволяют выбрать тип источника данных и формат, в котором эти данные поставляются. Кроме того, при печати документа желательно указать ориентацию листа (книжную или альбомную), размеры полей на листе и количество копий при печати. Эти и другие подобные им настройки печати хранятся в объектах типа:
- DocAttributeSet — объединяет свойства страницы (поля, размеры)
- PrintRequestAttributeSet — объединяет свойства печати (количество копий, качество печати)
и других
Эти интерфейсные классы хранятся в пакете javax.print.attribute и описывают коллекции свойств принтера и листа. Сами свойства описываются классами из пакета javax.print.attribute.standard. В частности:
- MediaSize — задает размеры листа
- MediaPrintableArea — задает размеры области печати на листе
- OrientationRequested — указывает орниентацию листа
- Copies — задает количество копий при печати
- PrintQuality — указывает качество печати: низкое/нормальное/высокое
- JobName — указывает имя задания в списке заданий на печать
и другие
Попробуем создать объекты с описанием свойств страницы и печати:
6.6 Связывание медиа-типа документа и свойств документа с источником данных
Связывание источника данных для печати с настройками страницы, которые необходимо использовать при печати, обычно осуществляется с помощью класса javax.print.SimpleDoc, реализующего интерфейс javax.print.Doc.
Я — относительно новый программист, так что это может быть очень простой вопрос, но его немного озадачило.
Я пытаюсь распечатать окончательный вывод графического интерфейса Java на принтер. Теперь, в моем графическом интерфейсе, у меня это есть, так что, когда вы нажимаете на печать, всплывающее окно выводит список доступных принтеров, и на основе того, который вы выбрали, он должен печатать на этом принтере.
Однако это не так. Я получил большую часть своего кода, прочесывая интернет для решения этой проблемы и нашел многообещающий код. Однако он распечатывается из файла. Поэтому все, что я просто делаю в своем методе, сначала записывает свой вывод в файл, чтобы я мог использовать ту же методологию.
Пара вещей перед методом:
Нет ошибок или исключений.
Файл, который я пытаюсь создать каждый раз, всегда существует и с правильным текстом.
Принтер, который я печатаю для получения задания на печать, даже считает, что он его выполнил.
Если бы я должен был догадаться, я бы подумал, что, возможно, я пишу вывод в файл так, что Принтер не будет, но не говорит мне. В любом случае, в этом коде есть совсем немного, что у меня нет четкого понимания, поэтому, пожалуйста, дайте мне знать, что вы можете найти.
«>





