Haiku BPrintJob: Page Geometry, Spooling, and Cancellation
Follow a Haiku print job from setup through page capture and spooling, accounting for printable geometry, continuation failures, cancellation, and testing.
Haiku’s BPrintJob is the application-facing Print Kit object for turning Interface Kit drawing into pages that can be sent to a configured printer or another print destination. It owns a job session and its spool state; it is not a printer driver, transport, or guarantee that paper has physically emerged. Applications should treat setup, page generation, spooling, and final commit as separate phases, and should make cancellation and errors visible.
This distinction is easy to miss because a one-page sample can make printing look like a single DrawView() call. In a real document, the application must reconcile page ranges with document state, map document coordinates to the printer’s printable rectangle, avoid locking a live window while the user interacts with print setup, and stop cleanly if the print job reports that it cannot continue.
The job lifecycle is stateful
The public BPrintJob header exposes a constructor, ConfigJob(), ConfigPage(), BeginJob(), DrawView(), SpoolPage(), CanContinue(), CommitJob(), and CancelJob(). The setup calls can show user-facing configuration; BeginJob() starts the job phase; page drawing records content; SpoolPage() closes the current page into the spool; and finalization either commits the job or cancels it. The API’s BeginJob, SpoolPage, CommitJob, and CancelJob return void, so code must not invent a status return from those methods. CanContinue() is a key continuation/error signal.
The safe control flow is therefore not “call BeginJob, assume success, and print forever.” Check the status of setup operations, begin only when the user accepted configuration, generate one page at a time, ask whether continuation remains possible, and stop promptly on cancellation or printer/spool failure. Consult the current header and implementation for exact behavior on the target Haiku revision; legacy examples are useful orientation, not a replacement for version-specific API contracts.
An application should keep its document model stable while the print job is generated. If the user can edit during printing, render from an immutable snapshot or retain a revision and detect changes. Otherwise page one can describe an older state while page five includes edits made later. A snapshot also makes page-range calculations deterministic.
Configuration changes the geometry
ConfigPage() and ConfigJob() let the user select page and printer settings. Query PaperRect() and PrintableRect() after setup rather than assuming that A4 or Letter dimensions start at the origin or that the full sheet is printable. Printer hardware often has non-printable margins; drivers may expose a smaller usable area. GetResolution() reports horizontal and vertical dots per inch, which need not be identical.
Document points, printer pixels, and paper coordinates are different units. If the document layout uses a logical page rectangle, compute a transform that maps that rectangle into the printable rectangle while preserving aspect ratio unless the user explicitly chose a crop or stretch. Centering should use the printable area’s origin, not the physical paper’s origin. A transform error can create subtle clipping that only appears on the real device.
Do not use PaperRect() as the clip rectangle for content. It describes the sheet; PrintableRect() describes the region the print system says is usable. Keep the application’s own margins inside that region. Text that fit on screen may wrap differently under print fonts and resolution; a print renderer should layout against the selected page width, not scale a window screenshot.
Generate pages from document state
DrawView() records drawing from a view into the print job. That convenience does not mean an on-screen window should be resized, moved, or left locked during a long print. A robust application renders a print-specific view or detached document representation with explicit bounds. It should not depend on transient hover state, selection handles, blinking carets, or screen-only decoration unless those are intentionally part of the printed output.
For a multipage document, split content using the print layout engine, not by copying the same full-page view repeatedly. Track the requested first and last page, validate them against the snapshot’s page count, and honor page range selection. If a page becomes empty due to reflow, decide whether it should be omitted or intentionally emitted; do not accidentally spool an extra blank page after the final page.
A useful conceptual loop is:
// Illustrative control flow: use the current Print Kit headers and
// application-specific page renderer for the exact view and bounds.
if (job.ConfigPage() != B_OK || job.ConfigJob() != B_OK)
return; // user cancelled or setup failed
job.BeginJob();
for (int32 page = firstPage; page <= lastPage; ++page) {
if (!job.CanContinue()) {
job.CancelJob();
return;
}
RenderPageIntoPrintView(page, printableBounds);
job.DrawView(printView, printableBounds, printOrigin);
job.SpoolPage();
}
if (job.CanContinue())
job.CommitJob();
else
job.CancelJob();
This is a control-flow sketch rather than a drop-in function: RenderPageIntoPrintView and the coordinate transform belong to the application. The API header confirms the void/status signatures above; the exact setup order and continuation behavior should be checked against the Haiku version being built and the current BPrintJob implementation. A finished page in the spool still does not prove the printer accepted or physically completed the job.
Cancellation and failure are ordinary outcomes
The user may cancel either setup panel, the spool file may fail, or the print destination can be disconnected. Do not silently turn such outcomes into success. Keep the application’s source document untouched, close temporary render resources, and show a concise message that distinguishes “cancelled by user,” “could not prepare pages,” and “job was submitted but device completion is unknown.” A generic “printed” dialog after CommitJob() is misleading if the API only committed the job to a spooler.
Do not call cancellation after every successful completion; make the lifecycle explicit and ensure each exit path finalizes once. Prefer a small job-state wrapper or scope guard so exceptions and early returns cannot leave a spool session partially open. Since several finalization methods are void, a wrapper can ensure cleanup but cannot conjure a detailed device status that the API does not expose.
Printing should not hold the application’s main window lock while a dialog is open or while pages are being produced. Prepare any required snapshot first, release the lock, and send a completion message back to the looper if the job runs in a worker. If the worker reads mutable view state, the code can deadlock with window shutdown or render inconsistent output.
Settings and reproducibility
Settings() and SetSettings() expose archived print settings. Treat these as user preferences that may become invalid when printer, media, or driver choices change. Validate with IsSettingsMessageValid() before restoring an archived message. Avoid writing undocumented private fields into a BMessage; store only settings accepted by the public API.
For reproducible bug reports, record Haiku revision, printer/driver/transport, paper size, resolution, selected page range, and whether output was preview, file, or physical device. If a spool is wrong but preview is correct, the defect may be downstream of app rendering. If preview is wrong too, compare the document snapshot and print transform before changing printer configuration.
Test page boundaries and device-independent rendering
Test one page, first/last page selection, an empty document, a very long document, and a page whose content touches each printable edge. Include Unicode text, embedded images, mixed orientations, and content that reflows at print width. Verify that the page count matches the UI, no extra blank sheet is produced, and the bottom/right edge is not clipped.
Test cancel at both configuration dialogs and during page generation if the application exposes a cancel control. Simulate an unavailable destination where feasible and verify that the document remains unchanged and the application remains responsive. Run the same test with Preview or print-to-file to separate application layout from printer transport.
BPrintJob is best understood as a stateful document-to-spool adapter. The application supplies a stable document snapshot and page renderer; Print Kit supplies configured page geometry and captures drawing; the system’s print server, driver, and transport determine what happens next. That separation keeps native printing testable and prevents UI success from being confused with physical delivery.
Related:
Sources: