Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Java’s 2D printing API is centered on PrinterJob. Create a job, provide either a Printable page renderer or a Pageable document description, optionally let the user choose settings, and submit the job with print(). The API lives in java.awt.print and is documented by Oracle as a general printing framework for page formats, document types, and job-control dialogs.

The Java printing model at a glance

Oracle’s Java API documentation calls PrinterJob “the principal class that controls printing.” A typical application performs these operations:

  1. Obtain a job with PrinterJob.getPrinterJob().
  2. Check or select the print service if the application needs to know where output will go.
  3. Attach content with setPrintable(...) or setPageable(...).
  4. Optionally show a print dialog.
  5. Call print(), or print(attributes) when using a request-attribute set.

Oracle’s PrinterJob reference documents the job lifecycle and print calls. The java.awt.print package overview describes the supporting page and document classes.

PrinterJob: the controller for a print operation

getPrinterJob() returns a job initially associated with the platform’s default printer. A default printer is not guaranteed. The method can still return a job when no printer is installed; in that case getPrintService() returns null, and a later print operation may fail.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
PrinterJob job = PrinterJob.getPrinterJob();

if (job.getPrintService() == null) {
    System.err.println("No default print service is available");
    // Offer printer discovery or a useful error to the user.
}

To discover available services rather than relying on the default, use lookupPrintServices():

PrintService[] services = PrinterJob.lookupPrintServices();
for (PrintService service : services) {
    System.out.println(service.getName());
}

The returned services and their capabilities depend on the operating system, installed drivers, and printer configuration.

Printable versus Pageable

Choose the interface that matches how your application knows about its document.

Question Printable Pageable
Who supplies the page count? Your callback is asked for page indexes; it returns NO_SUCH_PAGE when no page exists. The Pageable supplies a page count through getNumberOfPages().
Can formats differ by page? The callback receives a PageFormat for the job’s current format. Yes. getPageFormat(pageIndex) can return a different format for each page.
Where does rendering come from? The Printable.print(...) callback paints each requested page. getPrintable(pageIndex) supplies the painter for that page.
Best fit Streaming or generated output where the renderer can decide whether a page exists. A document with a known page count, page-specific formats, or different painters.

A Pageable is a document-level description: it provides the number of pages, each page’s format, and the Printable that renders that page. Book is the standard implementation for assembling pages that may use different formats or painters.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rendering pages with Printable

A Printable receives a graphics context, a PageFormat, and a zero-based page index. Return PAGE_EXISTS after painting a page and NO_SUCH_PAGE when the requested index is outside your document.

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

public final class HelloPrint implements Printable {
    @Override
    public int print(Graphics graphics, PageFormat format, int pageIndex)
            throws PrinterException {
        if (pageIndex > 0) {
            return NO_SUCH_PAGE;
        }

        Graphics2D g2 = (Graphics2D) graphics;
        g2.translate(format.getImageableX(), format.getImageableY());
        g2.drawString("Hello from Java printing", 0, 12);
        return PAGE_EXISTS;
    }

    public static void main(String[] args) throws PrinterException {
        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintable(new HelloPrint());
        if (job.printDialog()) {
            job.print();
        }
    }
}

The origin used for drawing is the graphics context’s coordinate system. Translating to getImageableX() and getImageableY() makes the example draw inside the printer’s usable region rather than assuming that the physical sheet starts at coordinate zero. Real renderers should also account for imageable width and height when wrapping or scaling content.

How page requests work

The print system may call your renderer more than once and may request indexes in an order determined by the implementation. Do not use a call as a one-time “start printing” signal. Keep rendering deterministic for a given page index, and return NO_SUCH_PAGE for indexes your document does not contain.

Describing a document with Pageable and Book

Use Pageable when page structure is part of the document model. A Book can append one painter for several pages or append individual painters with individual PageFormat objects.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

Book book = new Book();
PageFormat portrait = PrinterJob.getPrinterJob().defaultPage();
PageFormat landscape = PrinterJob.getPrinterJob().defaultPage();
landscape.setOrientation(PageFormat.LANDSCAPE);

Printable first = (graphics, format, page) -> {
    if (page != 0) return Printable.NO_SUCH_PAGE;
    graphics.drawString("Portrait page", (int) format.getImageableX(),
                        (int) format.getImageableY() + 12);
    return Printable.PAGE_EXISTS;
};

Printable second = (graphics, format, page) -> {
    if (page != 0) return Printable.NO_SUCH_PAGE;
    graphics.drawString("Landscape page", (int) format.getImageableX(),
                        (int) format.getImageableY() + 12);
    return Printable.PAGE_EXISTS;
};

book.append(first, portrait);
book.append(second, landscape);

PrinterJob job = PrinterJob.getPrinterJob();
job.setPageable(book);
job.print();

In production code, obtain and configure formats once, and avoid creating a second job merely to get a default format. The important distinction is that the Book owns page count and per-page descriptions, while each painter still performs the actual drawing.

PageFormat and Paper are different concepts

PageFormat describes how a page is laid out: its orientation, dimensions, and imageable area. Paper describes the physical paper characteristics used by that format. A sheet’s nominal width and height are not the same as the area a particular printer can mark; hardware margins can make the imageable rectangle smaller.

  • PageFormat: the layout view used by the renderer, including portrait, landscape, or reverse-landscape orientation.
  • Paper: the physical paper size and imageable bounds from which a format is built.
  • Imageable area: the region the printer reports as printable; keep text and graphics inside it unless you intentionally support borderless output.

Do not hard-code US Letter or A4 assumptions when the user or printer can select another media size. Read the format passed to print and calculate layout from its imageable coordinates and dimensions.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Validating a format for the selected printer

PrinterJob.validatePage(PageFormat) returns a copy adjusted for the current printer. Validation can reduce the imageable area to account for non-printable hardware margins; it is not a promise that every requested margin will be preserved.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
PageFormat requested = job.defaultPage();
PageFormat printerFormat = job.validatePage(requested);
job.setPrintable(new HelloPrint(), printerFormat);

When a user selects media or orientation in a dialog, validate or derive the format you actually use. Otherwise a document-level model may continue rendering with an old format even though the user chose different settings.

Dialogs, attributes, and cancellation

The no-argument printDialog() displays the platform print dialog and returns false if the user cancels. A dialog can throw HeadlessException in an environment without a graphical display, so server-side or batch applications should avoid UI dialogs and configure printing programmatically.

For request attributes, pass the same set returned or modified by the dialog into print(attributes):

PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
if (job.printDialog(attributes)) {
    job.print(attributes);
}

Showing a dialog does not automatically make every selected attribute part of the subsequent operation unless you use the attribute-aware print call. For a Pageable document that must honor selected media, derive the relevant PageFormat from those selections and update the document’s page descriptions before printing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A reliable application flow

  1. Choose the execution mode. In a desktop app, decide whether to show a dialog. In a headless process, require configured settings and handle the absence of a print service explicitly.
  2. Create and inspect the job. Call PrinterJob.getPrinterJob(); check getPrintService() or enumerate services with lookupPrintServices().
  3. Select the content model. Use setPrintable for callback-driven pages; use setPageable (often with Book) for known page counts or page-specific formats.
  4. Build layout from the supplied format. Use imageable coordinates and dimensions rather than the raw sheet edges.
  5. Apply user choices. If using attributes, pass them to print(attributes); update a Pageable format when its pages need to reflect selected media.
  6. Submit and handle failure. Call print() or print(attributes) and catch PrinterException. Treat missing services, cancellation, unsupported attributes, and driver errors as operational cases rather than rendering bugs.

Common mistakes to avoid

  • Assuming getPrinterJob() proves that a printer exists.
  • Using Printable as though it exposes an intrinsic page count; it does not.
  • Drawing from coordinate (0, 0) without considering the imageable origin.
  • Assuming all printers have identical margins or support every media and finishing attribute.
  • Ignoring the boolean result of printDialog(), which causes cancelled jobs to continue.
  • Calling a UI dialog from a headless service.
  • Expecting a dialog’s changed attributes to alter a Pageable format automatically.

What to remember

PrinterJob orchestrates the operation, Printable paints requested pages, and Pageable describes a document whose count and page formats can be queried. PageFormat and Paper express layout and physical media, but the selected printer ultimately constrains the imageable region. Building your renderer around the supplied format, validating it for the active service, and passing selected attributes to the actual print call are the foundations of predictable Java printing.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.