On the previous page, we talked about how the print driver calls for page content that it can send to the printer. But how do you supply this content? That’s where Graphics comes in.

In Java, anything that displays graphics and text is a Graphics object, and that includes the printed page. Now that we’ve discussed the relatively simple matter of getting a page to a printer, let’s discuss the Graphics class and how to “draw” text and graphics on a page.

Graphics and Graphics2D

When creating a printed page, your Java program will be provided an instance of the abstract Graphics class. In fact, it’s given an instance of Graphics’ subclass–also abstract–called Graphics2D, which has additional methods for working with two-dimensional graphics. The concrete class thus represented is an implementation provided by device drivers. But all you really need to know is that Graphics provides you with a component on which to draw shapes, lines, and text; and methods for doing so.

Now that you have the dimensions, of the printed page, you can draw on it–meaning, you can add images, shapes, and text. Here are just some of the methods that let you do that.

MethodFunction
void translate(int x, in y)Translates the origin of the graphics context to the point (x, y) in the current coordinate system. For example, if the current origin is (0, 0),
translate(36, 72)
effectively adds 36 to future x-coordinate values and 72 to future y-coordinate values.
boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)Initiates the process of Scaling and drawing img to fit within the indicated rectangle. observer is optional; ImageObserver is an optional interface for reporting on the progress of rendering the image.
void drawLine(int x1, int y1, int x2, int y2)Draws a line in the current color, from point (x1, y1) to point (x2, y2).
void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)Draws the outline of a circular or elliptical arc covering the specified rectangle.
void drawOval(int x, int y, int width, int height)Draws the outline of an oval.
void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
void drawPolygon(Polygon p)
Draws the outline of a polygon defined by the arrays of x and y coordinates, or Polygon p, respectively.
void drawRect(int x, int y, int width, int height)Draws the outline of a rectangle.
void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)Fills a circular or elliptical arc covering the specified rectangle.
void fillOval(int x, int y, int width, int height)Fills an oval.
void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
void fillPolygon(Polygon p)
Fills a polygon defined by the arrays of x and y coordinates, or Polygon p, respectively.
void fillRect(int x, int y, int width, int height)Fills a rectangle.
void drawString(String str, int x, int y)Draws str at the indicated coordinates, in the current color and font. The y coordinate specifies the location of the text baseline.
Font getFont()
void setFont(Font f)
Gets/sets the current Font.
FontMetrics getFontMetrics()
FontMetrics getFontMetrics(Font f)
Returns the FontMetrics for the context’s current font, or font f, respectively.
setClip(int x, int y, int width, int height)Sets the current clip to the indicated rectangle. Graphics painted outside the current clip are not rendered. Unless changed, the clip is the entire imageable area.
void setColor(Color c)Sets the current color to c.
Selected methods of Graphics

And there are many, many more which we urge you to explore.

Graphics2D, which is the actual class of the Graphics object you’re given when printing, has all these methods and more that perform image transformations, rotations, and variations of stroke and fill. But we’ll just mention two.

MethodFunction
Color getBackground()
void setBackground(Color c)
Retrieves/sets the current background color used for clearing a region.
Selected methods of Graphics2D

Font

A Graphics object has an associated Font, which of course determines the appearance of text drawn on the graphics context. So before drawing text, you’ll want to establish which font will be used and its appearance, changing the font as needed for different sizes and styles.

Here are some examples of Font creation:

Font font1 = new Font(Font.SERIF, Font.PLAIN, 10);
Font font2 = new Font(Font.SANS_SERIF, Font.PLAIN, 5);

In each case, the first argument is a font name, the second is a style, and the third is a point size.

The Font class provides these constants for generic font names:

  • Font.SERIF
  • Font.SANS_SERIF
  • DIALOG
  • DIALOG_INPUT
  • MONOSPACED

But if you know the name of a font available on your system, you can pass its name. You can also load fonts from input streams and files, and then create other fonts from them.

There are also three style constants:

  • Font.PLAIN
  • Font.BOLD
  • Font.ITALIC

When creating a Font object, you can choose any of these plus Font.BOLD + Font.ITALIC.

Here are some of the more useful methods of Font.

MethodFunction
static Font createFont(int fontFormat, File fontFile)
static Font createFont(int fontFormat, InputStream fontStream)
Creates a new font of the specified style by reading it from the File or InputStream.
fontFormat is either Font.TRUETYPE_FONT or Font.TYPE1_FONT. And to be made available to constructors, the font must be passed to GraphicsEnvironment.registerFont().
Font deriveFont(float size)
Font deriveFont(int style)
Font deriveFont(int style, float size)
Creates a new font from the current one, substituting a new font size and/or style. For example, to alternate between plain and italic text, you might code something like this:
Font plainFont = new Font(Font.SERIF, Font.PLAIN, 10);
graphics.drawString("This is plain. ");
Font italicFont = plainFont.deriveFont(Font.ITALIC);
graphics.drawString("This is italic.");
Selected methods of Font

FontMetrics

As you might expect, FontMetrics provides all kinds of measurements for a given Font: line height, ascent, descent, leading, and width of a given String.

Interestingly enough, getFontMetics() is a method of Graphics, not Font. But as shown above, you can ask for the metrics of either the current font or of a specific one.

Of its many methods, the two most useful are these:

MethodFunction
int getHeight()Returns the height of a line of text, in points
int stringWidth(String text)Returns the width of text, in points
Selected methods of FontMetrics

Putting It All Together

Now that we have a handle on the objects used in drawing text and graphics, let’s look at how we bring Lincoln to the page.

Page 1

    private void printPage1(Graphics graphics, PageFormat pageFormat) {
        // Add the photograph of Lincoln to the page. Scale it so
        // its width is IMAGE_WIDTH.
        float heightToWidth = ((float) address.getImage().getHeight(null)
                / ((float) (address.getImage().getWidth(null))));
        int targetImageHeight = (int) (IMAGE_WIDTH_IN_POINTS * heightToWidth);

        graphics.drawImage(address.getImage(), 0, 0, IMAGE_WIDTH_IN_POINTS, targetImageHeight, null);

        // Compute where the text will begin horizontally.
        int originX = IMAGE_WIDTH_IN_POINTS + 36;
        printText(graphics, pageFormat, originX, targetImageHeight, address);
    }

The first step is to add Lincoln’s portrait to the page. Note how the drawImage() method accepts the point on the page where the image’s upper left corner goes (0, 0), the width of the image, and the height.

Could we have used the image’s original width and height? Yes–but we can’t tell how that would translate to points. And we want to be able to print the speech text to the right of the image and under it without overlapping. The only way to know for sure where the image is is to control its horizontal and vertical end points ourselves. Thus we use a predefined target width and calculate a height. drawImage() scales the image to the allotted area for us.

Next, we compute where the text will start horizontally: 1/2 inch (36 points) to the right of the image. And finally, we call the printText() method to add the text.

    private void printText(Graphics graphics, PageFormat pageFormat, float originX, float imageHeight,
            GettysburgAddress address) {
        Font font = new Font(Font.SERIF, Font.PLAIN, 10);
        graphics.setFont(font);
        FontMetrics metrics = graphics.getFontMetrics();

        // Get the line height;
        int lineHeight = metrics.getHeight();

We start by creating our Font–SERIF PLAIN, 10-point–and establish it as the current font. Next we find the height of a text line in points.

Now we’ll put the words of the speech in a List. You’ll see why later.

        // Get the words of the text and convert the array to a List.
        String[] words = address.getWords();
        List<String> wordList = new ArrayList<String>(Arrays.asList(words));

Now determine where the first line of text begins.

        // Establish the starting x and y for the next line.
        int x = (int) (originX);
        int y = 0;

Finally, the loop that puts words to paper:

        // As long as there are words in the list, build a buffer
        // until the next word exceeds the maximum on the current
        // line. Then print the buffer as is, determine the next
        // y and x, and restart.
        StringBuilder buf = new StringBuilder();
        double textLimit = pageFormat.getImageableWidth() - x;
        while (!wordList.isEmpty()) {
            String nextWord = wordList.remove(0);
            if (metrics.stringWidth(buf.toString() + " " + nextWord) > textLimit) {
                graphics.drawString(buf.toString(), x, y);
                buf = new StringBuilder();
                y += lineHeight;
                if (y > imageHeight + lineHeight) {
                    x = 0;
                } else {
                    x = (int) (originX + 6);
                }
                textLimit = pageFormat.getImageableWidth() - x;
            }
            buf.append(nextWord + " ");
        }
        graphics.drawString(buf.toString(), x, y);

And now it’s a matter of popping words off the list and checking whether adding it to a buffer would yield too much text to fit on the current print line. When the word list is exhausted, we add whatever text has accumulated in the buffer, and we’re done. Our first page looks like this:

Page 1

Page 2

And now, just to get your feet wet with drawing shapes, the second page is produced so:

    private void printPage2(Graphics graphics, PageFormat pageFormat) {
        Graphics2D g2d = (Graphics2D) graphics;

        // Draw a 1-inch blue line.
        graphics.setColor(Color.BLUE);
        graphics.drawLine(0, 72, 72, 72);

        // Draw a 2-inch square with a red border and yellow fill.
        g2d.setColor(Color.YELLOW);
        graphics.fillRect(8, 120, 144, 144);
        graphics.setColor(Color.RED);
        graphics.drawRect(8, 120, 144, 144);

        // Draw a black circle with 1-inch radius and a line width of 3.
        graphics.setColor(Color.BLACK);
        g2d.setStroke(new BasicStroke());
        graphics.drawOval(8, 300, 72, 72);
    }

Notice how we change the color before drawing a line, the interior of a rectangle (fillRect()), the border of a rectangle, and a circle–which is just an oval with equal height and width. The result looks like this:

Page 2

Of course, in a batch environment, nobody is standing by to select a printer or approve it. So our next lesson is producing this same printout as a PDF using PdfBox from the Apache project.

Click here for the next step.