openpencil/packages/core/src/headless-render.ts
Danila Poyarkov 7d9eb2931c Move font loading and layout into SkiaRenderer.prepareForExport
The headless render path duplicated font collection, text measurer
setup, and layout recomputation separately from the renderer. Move
this into SkiaRenderer.prepareForExport() so browser and headless
share the same code path for export preparation.

Remove the now-redundant loadFonts from CLI headless and the manual
setTextMeasurer/collectFontKeys/computeAllLayouts from headless-render.
2026-03-14 12:06:46 +03:00

59 lines
2 KiB
TypeScript

import type { CanvasKit } from 'canvaskit-wasm'
import { renderNodesToImage, renderThumbnail, type ExportFormat } from './render-image'
import { SkiaRenderer } from './renderer'
import type { SceneGraph } from './scene-graph'
let cachedCk: CanvasKit | null = null
let cachedRenderer: SkiaRenderer | null = null
export async function initCanvasKit(): Promise<CanvasKit> {
if (cachedCk) return cachedCk
const CanvasKitInit = (await import('canvaskit-wasm/full')).default
const ckPath = import.meta.resolve('canvaskit-wasm/full')
const binDir = new URL('.', ckPath).pathname
cachedCk = await CanvasKitInit({ locateFile: (file: string) => binDir + file })
return cachedCk
}
async function getRenderer(): Promise<{ ck: CanvasKit; renderer: SkiaRenderer }> {
const ck = await initCanvasKit()
if (cachedRenderer) return { ck, renderer: cachedRenderer }
const surface = ck.MakeSurface(1, 1)
if (!surface) throw new Error('Failed to create CanvasKit surface')
const renderer = new SkiaRenderer(ck, surface)
renderer.viewportWidth = 1
renderer.viewportHeight = 1
renderer.dpr = 1
await renderer.loadFonts()
cachedRenderer = renderer
return { ck, renderer }
}
export async function headlessRenderNodes(
graph: SceneGraph,
pageId: string,
nodeIds: string[],
options: { scale?: number; format?: ExportFormat; quality?: number } = {}
): Promise<Uint8Array | null> {
const { ck, renderer } = await getRenderer()
await renderer.prepareForExport(graph, pageId, nodeIds)
return renderNodesToImage(ck, renderer, graph, pageId, nodeIds, {
scale: options.scale ?? 1,
format: options.format ?? 'PNG',
quality: options.quality
})
}
export async function headlessRenderThumbnail(
graph: SceneGraph,
pageId: string,
width: number,
height: number
): Promise<Uint8Array | null> {
const { ck, renderer } = await getRenderer()
const page = graph.getNode(pageId)
if (page) await renderer.prepareForExport(graph, pageId, page.childIds)
return renderThumbnail(ck, renderer, graph, pageId, width, height)
}