CoreGraphicsCanvas
KiteCanvas backed by an iOS / macOS CGContextRef. Pure CoreGraphics: no Compose, no Skia. The natural choice for native iOS apps using UIKit or SwiftUI; pass the context from your custom UIView's drawRect: or the UIGraphicsImageRenderer.image { ctx in … } block straight in.
Pair with ApplePdfRasterizer for the "give me a PNG" headless use case.
Memory: every Core Foundation / Core Graphics ref we allocate (gradient, colour space, image, CFData) is paired with a release call. Nothing escapes. Kotlin/Native objects don't leak, and we never hand a CG ref to caller code.
Properties
True when this canvas needs resolved glyph outlines (TextGlyph.outline), i.e. it actually paints (a render backend). Extraction canvases that only read text + advances override this to false, letting the driver skip the cost of resolving glyph outlines. Such a canvas gets every text run through drawGlyphs, also a run that paints nothing, such as invisible OCR text.
Functions
Apply a soft mask to the content rendered inside render (ISO 32000-1 §11.6.5). The default implementation just calls render. Backends without offscreen compositing fall back to painting through the mask.
ISO 32000-1, 11.6.5.2: the mask group draws into a bitmap of its own. Its alpha, or its luminosity over a black backdrop, becomes a grey mask that the content paints through with CGContextClipToMask. So the mask group's colours never reach the page, and each paint keeps its own blend mode against the page (#79). The transfer table maps each value of the grey mask.
Set up for a page render. The renderer calls this once before any draw call, with the page dimensions (in PDF user units, 1pt = 1/72 inch) and the desired device CTM (e.g. flip Y and scale to fit a target rectangle).
A non-isolated group at full alpha in Normal paints straight onto its backdrop, so its blend modes see what lies under it (ISO 32000-1, 11.4.5, #125). Any other group paints into a transparency layer. A transparency layer always starts transparent, which is exact for a non-isolated group only when no paint inside blends. In a knockout group (11.4.6) each paint replaces what lies under it inside its shape.
Paint one text run, already laid out into glyphs by the document handler. Format-neutral: no PDF (or other) font object crosses the seam, so every handler (PDF, EPUB, and others) drives text the same way.
Paint an XObject Image under ctm. PDF defines the image's bounds as the unit square (0..1, 0..1); the CTM already encodes the scale + position. A canvas paints the pixels of a KiteImageData.Kind.RAW image and draws a placeholder for every other kind. It never throws (#184).
Paint an XObject Image as drawImage does, composited with blendMode. The blend mode of the graphics state applies to an image as to every other paint (ISO 32000-1, 11.3.5).
Fill path under ctm with color at alpha (0..1) under the given blendMode. The default alpha = 1.0 + blendMode = Normal is the plain over-paint that PDF assumes when no ExtGState modifies the state.
Fill clipPath (under ctm) with the gradient defined by shading. If clipPath is null the shading covers the current clip region (or the page, when no clip is active). That is the spec's sh operator.
The outline of text in the host face this canvas draws a font without embedded outlines in, chosen from fontSpec as drawGlyphs chooses it. The outline is in glyph space: 1000 units per em, y up, and the pen at the origin on the baseline. A document handler strokes and clips such text with it, since a missing font program does not excuse the stroke (ISO 32000-1, 9.3.6).
Push a clip to path under ctm. Matched 1:1 by popClip.
The raster step (#209, #308): its box is region under ctm in device pixels, cut to the clip. A render paints into a bitmap context of the box, as a soft mask's group does. The backdrop is a snapshot of the host when it is a bitmap context with no transparency layer open. Past MASK_PIXEL_BUDGET the step works at a lower resolution, and then reads no backdrop. A raster's first row is the top of the box. Quartz's base space runs up, but a context's device space can run either way, and a bitmap context's runs down, so the step reads which way from the context (#308).
Stroke path under ctm with color at lineWidth user-units, alpha, blendMode. lineCap 0/1/2 = butt/round/square; lineJoin 0/1/2 = miter/round/bevel.