fix(canvas): render derived text without fonts
This commit is contained in:
parent
977ccacf41
commit
ff85febddb
|
|
@ -6,8 +6,8 @@ import type { Color } from '#core/types'
|
|||
import { vectorNetworkToCenterlinePath } from '#core/vector'
|
||||
|
||||
import { renderBooleanOperation } from './boolean'
|
||||
import { nodeHasRadius } from './shapes'
|
||||
import type { SkiaRenderer, RenderOverlays } from './renderer'
|
||||
import { nodeHasRadius } from './shapes'
|
||||
import {
|
||||
drawDashedRRectWithSolidCorners,
|
||||
drawStyledRRectStroke,
|
||||
|
|
@ -557,15 +557,6 @@ export function renderText(r: SkiaRenderer, canvas: Canvas, node: SceneNode, fil
|
|||
}
|
||||
|
||||
const paragraphY = 0
|
||||
if (!r.isNodeFontLoaded(node)) {
|
||||
canvas.restore()
|
||||
return
|
||||
}
|
||||
if (isGradientFill(fill) && drawGradientText(r, canvas, node, paragraphY)) {
|
||||
canvas.restore()
|
||||
return
|
||||
}
|
||||
|
||||
if (node.textPicture) {
|
||||
const pic = r.ck.MakePicture(node.textPicture)
|
||||
if (pic) {
|
||||
|
|
@ -579,6 +570,15 @@ export function renderText(r: SkiaRenderer, canvas: Canvas, node: SceneNode, fil
|
|||
canvas.restore()
|
||||
return
|
||||
}
|
||||
|
||||
if (!r.isNodeFontLoaded(node)) {
|
||||
canvas.restore()
|
||||
return
|
||||
}
|
||||
if (isGradientFill(fill) && drawGradientText(r, canvas, node, paragraphY)) {
|
||||
canvas.restore()
|
||||
return
|
||||
}
|
||||
if (r.fontsLoaded && r.fontProvider) {
|
||||
const paragraph = r.buildParagraph(node, r.fillPaint.getColor())
|
||||
canvas.drawParagraph(paragraph, 0, paragraphY)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,98 @@
|
|||
import { describe, expect, test } from 'bun:test'
|
||||
import { beforeAll, describe, expect, test } from 'bun:test'
|
||||
|
||||
import { renderNodesToImage, SceneGraph, SkiaRenderer } from '@open-pencil/core'
|
||||
|
||||
import { initCanvasKit } from '#cli/headless'
|
||||
import { snapFigmaDerivedGlyphBaseline } from '#core/canvas/text-derived'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
let ck: Awaited<ReturnType<typeof initCanvasKit>>
|
||||
|
||||
beforeAll(async () => {
|
||||
ck = await initCanvasKit()
|
||||
})
|
||||
|
||||
function squareCommandsBlob(): Uint8Array {
|
||||
const blob = new Uint8Array(1 + 4 * 9 + 1)
|
||||
const view = new DataView(blob.buffer)
|
||||
let offset = 0
|
||||
const commands = [
|
||||
{ command: 1, x: 0, y: 0 },
|
||||
{ command: 2, x: 1, y: 0 },
|
||||
{ command: 2, x: 1, y: 1 },
|
||||
{ command: 2, x: 0, y: 1 }
|
||||
]
|
||||
for (const { command, x, y } of commands) {
|
||||
blob[offset] = command
|
||||
view.setFloat32(offset + 1, x, true)
|
||||
view.setFloat32(offset + 5, y, true)
|
||||
offset += 9
|
||||
}
|
||||
blob[offset] = 0
|
||||
return blob
|
||||
}
|
||||
|
||||
describe('derived text rendering', () => {
|
||||
test('snaps Figma glyph baselines to device pixels', () => {
|
||||
expect(snapFigmaDerivedGlyphBaseline(47.45454406738281)).toBe(47)
|
||||
expect(snapFigmaDerivedGlyphBaseline(15.090909004211426)).toBe(15)
|
||||
expect(snapFigmaDerivedGlyphBaseline(17.81818199157715)).toBe(18)
|
||||
})
|
||||
|
||||
test('draws Figma-derived glyphs even when the font is unavailable', async () => {
|
||||
const graph = new SceneGraph()
|
||||
const page = graph.getPages()[0]
|
||||
const text = graph.createNode('TEXT', page.id, {
|
||||
width: 20,
|
||||
height: 20,
|
||||
text: 'x',
|
||||
fontFamily: '__MissingFont__',
|
||||
fills: [
|
||||
{
|
||||
type: 'SOLID',
|
||||
color: { r: 0, g: 0, b: 0, a: 1 },
|
||||
opacity: 1,
|
||||
visible: true
|
||||
}
|
||||
],
|
||||
figmaDerivedTextGlyphs: [
|
||||
{
|
||||
commandsBlob: squareCommandsBlob(),
|
||||
x: 2,
|
||||
y: 12,
|
||||
fontSize: 10
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const surface = expectDefined(ck.MakeSurface(1, 1), 'surface')
|
||||
const renderer = new SkiaRenderer(ck, surface)
|
||||
|
||||
try {
|
||||
const png = expectDefined(
|
||||
renderNodesToImage(ck, renderer, graph, page.id, [text.id], {
|
||||
scale: 1,
|
||||
format: 'PNG'
|
||||
}),
|
||||
'png'
|
||||
)
|
||||
const image = expectDefined(ck.MakeImageFromEncoded(png), 'image')
|
||||
const pixels = expectDefined(
|
||||
image.readPixels(0, 0, {
|
||||
alphaType: ck.AlphaType.Unpremul,
|
||||
colorType: ck.ColorType.RGBA_8888,
|
||||
colorSpace: ck.ColorSpace.SRGB,
|
||||
width: image.width(),
|
||||
height: image.height()
|
||||
}),
|
||||
'pixels'
|
||||
)
|
||||
|
||||
expect(Math.max(...pixels.filter((_, index) => index % 4 === 3))).toBeGreaterThan(0)
|
||||
image.delete()
|
||||
} finally {
|
||||
surface.delete()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue