Welcome to the first article in a five-part series on printing in Java. In this series, you will learn the strengths and weaknesses of the Java printing API. My goal is to help you build a framework that will work on top of the API to ease the burden of creating printed output. This framework will allow you to create pages with running headers/footers, and insert paragraphs, images, and tables.
This month, I will explain the terminology used in printing and introduce the Java printing model and API. As you will see throughout this series, printing using the API is not easy; rendering complex pages using a higher-level API might be helpful. That is why our goal will be to build a framework that will provide all the fundamental functionality required to effortlessly render pages. But first, let's learn the basics.
|Printing in Java: Read the whole series!|
Definition of a page
Before we dive into the technicalities of the printing API, let's start by defining some terminology that I will use throughout this series. Although this terminology might seem trivial, it will help clear up some confusion about margins.
As you probably know, Gutenberg invented the first printing press. At that time, he had to create a terminology to describe the layout of a page. Here is Gutenberg's definition of a page:
In Figures 1 and 2, we can see that the page is divided into several areas. The printer margins make up the page's periphery. They are printer-dependent and define the minimum area that the printer needs to feed the page. Printer margins are not user-definable. We seldom use or know the sizes of printer margins, but some printer manufacturers will publish them in their user manuals. In Java, you do not need to know these measurements; the API returns the dimensions of the printable area.
Just inside the printer margins are the margins that define the contour of the page. Notice that the left and right margins extend the length of the page minus the top and bottom printer margins. The gutter on the left side of the page provides an additional margin that is used primarily for binding the pages in a book. On a page printed in duplex mode -- that is, with printing on both sides -- the gutter can also be found on the page's right side. To obtain the total usable left or right margin, you add the gutter to either the left or right margin. The printing API itself does not support the gutter, but our print framework will enable you to define one. As strange as it may seem, the print API also fails to support margins. The only way to set margins is to set the location and size of the printable area.
Finally, the area in the middle of the page is called the printable area. At first glance, the page layout might look similar to the
BorderLayout that we are accustomed to. However, in the
BorderLayout, both top and bottom components extend the width of the display area, whereas in the physical page layout, the top and bottom margins are contained between the left and right margins.
Units of measurement
When working with the
Graphics2D class, it is important to understand the difference between device space and user space. In the device space, you work in pixels using the resolution of the device. A square of 100 pixels by 100 pixels drawn on a device that has a resolution of 1,024 pixels by 768 pixels will not be the same size as it is when rendered on a device that has a resolution of 1,600 pixels by 1,400 pixels. The reason is simple: because the second device features more pixels per inch, the square will appear smaller.
User space, on the other hand, allows us to think in terms of measurement units, regardless of the device's resolution. When you create a
Graphics2D object for a given device (printer or screen), a default transform is generated to map the user space to the device space. In user space, the default is set to 72 coordinates per inch. Instead of thinking in terms of pixels, you think in terms of units. A 1-by-1-inch square is 72 units by 72 units. A letter-size page (8.5 by 11 inches) is 612 by 792 points. When using the print API, you must set your mind to work with units because all the classes work in the user space.
Java printing system
The Java printing system has evolved considerably in its last two releases. Starting with version 1.2, the printing system allows you to use the Java 2D API -- one of the most advanced graphical APIs built as part of a programming language -- to render a page. This 2D API allows whatever is drawn on the screen to be rendered on paper.
Although more advanced now, the printing API still only supports the printer currently selected by the user at any given time. Java does not support printer discovery -- obtaining a list of available printers and their features on a given computer. Available printers can either be local or networked. When using the API, no way exists for obtaining a printer list programmatically; only if the print dialog is displayed can the user select a printer. This is a feature that Sun, which is adhering to the Internet Printing Protocol, will address in the next version of Java (1.4).
The printing model changed completely in Java 1.2. In previous versions of Java, the rendering process was not optimized at all. In Java 1.1 for example, printing a simple page required a great deal of memory and was very slow. Java 1.2 streamlined and optimized the rendering process. This redesigned API is based on a callback model, in which the printing subsystem, not your program, controls when a page is rendered. This model is more object-oriented in nature than the one used in JDK 1.1, in which the application was in charge of the printing process.
To simplify the concept, let's say that your program has a contract with the printing subsystem to supply a given page at a given time. The printing subsystem may request that your application render a page more than once, or render pages out of sequence. This model provides several advantages. First, by sending strips of the page instead of the whole page to the printer, it allows the application to print complex documents that would require more printer memory than is available. The application does not have to know how to print each strip; it only needs to know how to render a given page. The API will take care of the rest. In this case, the printing subsystem might request that a page be rendered several times depending on the number of strips required to completely print the page. Second, if the paper tray on a particular printer outputs the pages in reverse order, then your application might be asked to print the document in reverse order, so it will appear in the right order in the output tray.