Core Graphics PDF Generation on macOS: Pages, Metadata, and Finalization
Generate dependable multi-page PDFs with Core Graphics by defining page boxes, closing contexts, embedding metadata, and validating staged output.
Core Graphics can create a PDF by directing drawing commands into a PDF graphics context. The same drawing APIs can target windows, bitmap images, printers, or a PDF file, but a PDF context has additional lifecycle requirements: define page bounds, begin and end every page, supply metadata where useful, and close the document before treating the file as complete.
PDF generation is not just a drawing problem. It is also a page-geometry contract, a resource-lifetime problem, and a publication workflow. A file that exists at the destination path can still be incomplete if the context was never closed, if a page boundary was omitted, or if a later drawing operation failed after a partial write.
Create a URL-backed PDF context
The URL-based CGContext initializer creates a PDF graphics context for a destination URL. Its media box defines the default page size in points. An 8.5-by-11-inch page is commonly represented as 612 by 792 points, but the right size depends on the document’s intended use. Use explicit dimensions and units rather than copying dimensions from a pixel-based image.
import CoreGraphics
import Foundation
func createOnePagePDF(at destination: URL) throws {
var mediaBox = CGRect(x: 0, y: 0, width: 612, height: 792)
let metadata: [CFString: Any] = [
kCGPDFContextTitle: "Generated Report" as CFString,
kCGPDFContextCreator: "Example macOS Application" as CFString
]
guard let context = CGContext(
destination as CFURL,
mediaBox: &mediaBox,
metadata as CFDictionary
) else {
throw NSError(domain: "PDFGeneration", code: 1)
}
context.beginPDFPage(nil)
context.setFillColor(CGColor(gray: 0.95, alpha: 1.0))
context.fill(CGRect(x: 36, y: 36, width: 540, height: 720))
context.endPDFPage()
context.closePDF()
}
This example draws a simple page and closes the context. The page fill is illustrative; production content needs typography, graphics, and layout measured against the selected page boxes. Treat failure to create the context as a generation error, and do not tell the user the PDF was saved until page drawing and closePDF() have both completed.
Bracket pages and define boxes
Call beginPDFPage and endPDFPage for every page. Drawing outside a page boundary in a page-based context is ignored according to Apple’s Quartz documentation. For page-specific media, crop, bleed, trim, or art boxes, pass a page dictionary when beginning each page. Keep those boxes consistent with how downstream viewers, printers, and production workflows interpret the document.
The media box defines the physical extent of the page. Crop, bleed, trim, and art boxes describe different intended regions. They are not interchangeable names for the same rectangle. A report that only targets screen reading may only need a media box; print-ready output may need more deliberate page-box and output-intent treatment. Consult the requirements of the printer or PDF/X workflow rather than inventing box values from visual appearance.
Establish a drawing coordinate convention for each page. Quartz contexts have transformations and coordinate systems that can differ from AppKit view drawing. If a shared drawing function is used for both screen and PDF output, pass an explicit transform and page layout so text baselines, image orientation, and origin behavior are consistent. Avoid sprinkling unexplained vertical flips around rendering code.
For long documents, generate pages incrementally rather than rendering the entire document into one bitmap and embedding that image. Vector drawing keeps text and paths scalable where the content permits it, while raster images should be sized for the intended output. Bound memory by processing one page or a small batch at a time.
Metadata is part of the artifact
The PDF context accepts document metadata such as title, author, creator, subject, and keywords through its auxiliary dictionary. Set values from trusted app data and keep them accurate. Metadata is visible in document inspectors and search results, so do not place private paths, account IDs, or secrets into fields such as title or keywords.
Metadata does not guarantee accessibility or PDF/A conformance. Searchable text, reading order, tagged structure, embedded fonts, color profile, and archival conformance are separate requirements. Core Graphics drawing alone does not automatically establish that a generated PDF satisfies a regulated or archival format. Validate against the target standard using appropriate tooling.
An output intent can communicate the intended color reproduction condition. Only include one when the selected profile and production condition are accurate. A generic display profile is not automatically the right output intent for a commercial printer. Consult the receiving workflow and verify the output with its prescribed preflight process.
Close before publishing
closePDF() closes the PDF document. Treat it as a required finalization step, not merely a convenience. If drawing code throws or exits early, use a cleanup path that closes the context. Write to a temporary file in the destination volume, close the context, validate the result, and then atomically replace the final destination where the file system and app’s document model allow it.
Do not leave a half-written file under the user’s final filename. A safer flow is create staging file -> draw pages -> close -> reopen and inspect -> move or replace final artifact. If the process is interrupted, clean up or quarantine the staging file without deleting a previously valid document. Preserve the existing destination until the new output has passed validation.
If a PDF is generated as part of a document save, coordinate the output with the document’s normal save lifecycle. Avoid having one background task overwrite a file that another window is editing. Use a revision token to ensure the PDF corresponds to the model version that was actually saved.
Links and destinations
Core Graphics supports PDF destinations and URL annotations. These can make a generated table of contents or reference list navigable. Add destinations only after deciding stable names and target points, and associate links with the correct page rectangles. Ensure external links use canonical, verified URLs and internal destinations actually exist in the final document.
Test link hit areas at different zoom levels and in more than one PDF viewer. A URL annotation does not guarantee that a viewer will open the target automatically; user and application preferences control that interaction. Keep link text visible so the document remains usable in print or in viewers that don’t expose interactive links.
Typography, images, and color
Text rendering needs a deliberate font and line-break policy. Long localized text, font fallback, and accessibility-sized content can produce different page counts. Measure text within the actual page content box and keep paragraph layout separate from drawing. For a multi-page report, maintain a layout cursor that advances based on measured content rather than hard-coded line counts.
Images should be downsampled to an appropriate resolution before inclusion when the source is extremely large. Preserve an embedded color profile where required and decide how alpha is composited onto the page. A PDF that renders correctly on one display can print differently if its color spaces and output intent do not match the production workflow.
Validation matrix
Test empty content, one page, a page break at an exact boundary, oversized images, missing fonts, long unbreakable strings, transparent imagery, a multi-page document, an unwritable destination, disk full during generation, cancellation during page drawing, and app termination before close. Reopen generated PDFs and check page count, media boxes, metadata, text extraction, representative links, and final file size.
Use a PDF parser or PDFKit to inspect the result after closing. A nonzero file size is not proof of a valid document. For visually sensitive exports, rasterize representative pages and compare layout; for print workflows, run the required preflight and proofing steps. Record the generation version and source model revision so a support report can reproduce the artifact.
Core Graphics provides a flexible drawing target and a PDF context lifecycle. The application remains responsible for page layout, metadata hygiene, accessibility and conformance requirements, staging, validation, and atomic publication. Make finalization an explicit checkpoint so incomplete output cannot masquerade as a successfully saved report.
Related:
- PDFKit on macOS: Document Ownership, Search, Selections, and Annotations
- AppKit Printing on macOS: NSPrintOperation, Page Geometry, and Pagination
Sources: