- Reject text pictures captured before the current font generation - Keep text visible when CanvasKit must synthesize a missing italic face
113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
|
|
import type { CanvasKit, TypefaceFontProvider } from 'canvaskit-wasm'
|
|
|
|
import { FontManager, fontManager } from '@open-pencil/core/text'
|
|
import { SceneGraph } from '@open-pencil/scene-graph'
|
|
|
|
import { isTextPictureCurrent } from '#core/canvas/renderer/fonts'
|
|
import { nodeFontReadiness } from '#core/canvas/text'
|
|
import { fontFaceDemand, fontResolver } from '#core/text/resolver'
|
|
|
|
function pageId(graph: SceneGraph): string {
|
|
return graph.getPages()[0].id
|
|
}
|
|
|
|
describe('font lifecycle', () => {
|
|
test('advances generation only for provider epochs and unique registrations', () => {
|
|
const manager = new FontManager()
|
|
const registrations: string[] = []
|
|
const provider = {
|
|
registerFont(_data: ArrayBuffer, family: string) {
|
|
registrations.push(family)
|
|
}
|
|
} as TypefaceFontProvider
|
|
const data = new ArrayBuffer(12)
|
|
|
|
expect(manager.generation()).toBe(0)
|
|
manager.attachProvider({} as CanvasKit, provider)
|
|
const providerGeneration = manager.generation()
|
|
manager.markLoaded('Generation Test', 'Regular', data)
|
|
const registrationGeneration = manager.generation()
|
|
manager.markLoaded('Generation Test', 'Regular', data)
|
|
|
|
expect(providerGeneration).toBeGreaterThan(0)
|
|
expect(registrationGeneration).toBeGreaterThan(providerGeneration)
|
|
expect(manager.generation()).toBe(registrationGeneration)
|
|
expect(registrations).toEqual(['Generation Test'])
|
|
})
|
|
|
|
test('keeps cumulative subset registrations under the source family', () => {
|
|
const manager = new FontManager()
|
|
const registrations: string[] = []
|
|
const provider = {
|
|
registerFont(_data: ArrayBuffer, family: string) {
|
|
registrations.push(family)
|
|
}
|
|
} as TypefaceFontProvider
|
|
|
|
manager.attachProvider({} as CanvasKit, provider)
|
|
manager.markLoaded('Subset Font', 'Regular', new ArrayBuffer(8))
|
|
const firstGeneration = manager.generation()
|
|
manager.markLoaded('Subset Font', 'Regular', new ArrayBuffer(12))
|
|
|
|
expect(manager.renderFamily('Subset Font', 'Regular')).toBe('Subset Font')
|
|
expect(manager.generation()).toBeGreaterThan(firstGeneration)
|
|
expect(registrations).toEqual(['Subset Font', 'Subset Font'])
|
|
})
|
|
|
|
test('tracks nodes gated by pre-render font resolution', () => {
|
|
const manager = new FontManager()
|
|
manager.blockNodesUntilFontsResolve(['first', 'second'])
|
|
expect(manager.isNodeBlocked('first')).toBe(true)
|
|
expect(manager.isNodeBlocked('second')).toBe(true)
|
|
manager.unblockNodes(['first'])
|
|
expect(manager.isNodeBlocked('first')).toBe(false)
|
|
expect(manager.isNodeBlocked('second')).toBe(true)
|
|
})
|
|
|
|
test('rejects a text picture observed before the font generation changed', () => {
|
|
const graph = new SceneGraph()
|
|
const node = graph.createNode('TEXT', pageId(graph), {
|
|
name: 'Cached fallback',
|
|
x: 0,
|
|
y: 0,
|
|
width: 100,
|
|
height: 20,
|
|
text: 'Hello',
|
|
textPicture: new Uint8Array([1, 2, 3])
|
|
})
|
|
const renderer = {
|
|
fontGeneration: 1,
|
|
textPictureGenerations: new Map<string, { data: Uint8Array; generation: number }>()
|
|
}
|
|
|
|
expect(isTextPictureCurrent(renderer, node)).toBe(true)
|
|
renderer.fontGeneration = 2
|
|
expect(isTextPictureCurrent(renderer, node)).toBe(false)
|
|
})
|
|
|
|
test('keeps text visible when an unavailable italic face can use a loaded family face', () => {
|
|
const family = 'Missing Italic Regression'
|
|
const demand = fontFaceDemand(family, 'Regular Italic', 'Hello')
|
|
fontResolver.reset(demand)
|
|
fontResolver.exhaust(demand)
|
|
fontManager.markLoaded(family, 'Regular', new ArrayBuffer(12))
|
|
|
|
const graph = new SceneGraph()
|
|
const node = graph.createNode('TEXT', pageId(graph), {
|
|
name: 'Synthetic italic',
|
|
x: 0,
|
|
y: 0,
|
|
width: 100,
|
|
height: 20,
|
|
text: 'Hello',
|
|
fontFamily: family,
|
|
italic: true
|
|
})
|
|
|
|
expect(nodeFontReadiness({}, node)).toBe('ready')
|
|
fontResolver.reset(demand)
|
|
})
|
|
})
|