fix(text): preserve requested font weights
This commit is contained in:
parent
b46722a50b
commit
5d71e810b6
|
|
@ -208,8 +208,8 @@ function addStyledRuns(
|
|||
),
|
||||
fontSize: runFontSize,
|
||||
fontStyle: {
|
||||
weight: { value: 400 } as FontWeight,
|
||||
slant: ck.FontSlant.Upright
|
||||
weight: { value: s.fontWeight ?? node.fontWeight } as FontWeight,
|
||||
slant: (s.italic ?? node.italic) ? ck.FontSlant.Italic : ck.FontSlant.Upright
|
||||
},
|
||||
letterSpacing: s.letterSpacing ?? (node.letterSpacing || 0),
|
||||
decoration: textDecorationValue(ck, s.textDecoration ?? node.textDecoration),
|
||||
|
|
@ -263,8 +263,8 @@ export function buildParagraph(
|
|||
),
|
||||
fontSize: baseFontSize,
|
||||
fontStyle: {
|
||||
weight: { value: 400 } as FontWeight,
|
||||
slant: ck.FontSlant.Upright
|
||||
weight: { value: node.fontWeight } as FontWeight,
|
||||
slant: node.italic ? ck.FontSlant.Italic : ck.FontSlant.Upright
|
||||
},
|
||||
letterSpacing: node.letterSpacing || 0,
|
||||
decoration: textDecorationValue(ck, node.textDecoration),
|
||||
|
|
|
|||
|
|
@ -45,16 +45,12 @@ export function chooseLocalFontMatch<T extends LocalFontMatch>(
|
|||
})
|
||||
if (sameStyle) return sameStyle
|
||||
|
||||
const sameSlant = candidates.filter((x) => parseFontStyle(x.style).italic === requested.italic)
|
||||
if (sameSlant.length > 0) {
|
||||
return sameSlant.sort(
|
||||
(a, b) =>
|
||||
Math.abs(parseFontStyle(a.style).weight - requested.weight) -
|
||||
Math.abs(parseFontStyle(b.style).weight - requested.weight)
|
||||
)[0]
|
||||
}
|
||||
if (style) continue
|
||||
|
||||
if (!style && candidates.length > 0) return candidates[0]
|
||||
const sameSlant = candidates.filter((x) => parseFontStyle(x.style).italic === requested.italic)
|
||||
if (sameSlant.length > 0) return sameSlant[0]
|
||||
|
||||
if (candidates.length > 0) return candidates[0]
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { initCanvasKit } from '#cli/headless'
|
|||
import type { SkiaRenderer } from '#core/canvas/renderer'
|
||||
import { renderText } from '#core/canvas/scene'
|
||||
import type { SceneNode } from '#core/scene-graph'
|
||||
import { buildParagraph } from '#core/canvas/text'
|
||||
import { fontManager } from '#core/text/fonts'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
|
@ -69,10 +70,27 @@ function textNode(overrides: Partial<SceneNode> = {}): SceneNode {
|
|||
text: 'Hello 你好',
|
||||
fontSize: 16,
|
||||
fontFamily: 'Arial',
|
||||
fontWeight: 400,
|
||||
italic: false,
|
||||
letterSpacing: 0,
|
||||
lineHeight: null,
|
||||
textAlignHorizontal: 'LEFT',
|
||||
textAlignVertical: 'TOP',
|
||||
textAutoResize: 'NONE',
|
||||
textDecoration: 'NONE',
|
||||
textDirection: 'AUTO',
|
||||
styleRuns: [],
|
||||
...overrides
|
||||
} as SceneNode
|
||||
}
|
||||
|
||||
async function createTextRenderer() {
|
||||
const ck = await initCanvasKit()
|
||||
const surface = expectDefined(ck.MakeSurface(400, 120), 'surface')
|
||||
const renderer = new SkiaRendererClass(ck, surface)
|
||||
return { renderer, surface }
|
||||
}
|
||||
|
||||
describe('renderText', () => {
|
||||
test('uses buildParagraph when fonts are loaded and node font is available', () => {
|
||||
const r = createMockRenderer()
|
||||
|
|
@ -150,6 +168,34 @@ describe('renderText', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('paragraph font weights', () => {
|
||||
test('bold Inter paragraph is wider than regular Inter', async () => {
|
||||
const { renderer, surface } = await createTextRenderer()
|
||||
await renderer.loadFonts()
|
||||
const regular = await Bun.file(repoPath('public/Inter-Regular.ttf')).arrayBuffer()
|
||||
const bold = await Bun.file(repoPath('public/Inter-Bold.ttf')).arrayBuffer()
|
||||
fontManager.markLoaded('Inter', 'Regular', regular)
|
||||
fontManager.markLoaded('Inter', 'Bold', bold)
|
||||
|
||||
const base = textNode({
|
||||
text: 'World largest design',
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 64,
|
||||
width: 1000,
|
||||
height: 100,
|
||||
fontWeight: 400,
|
||||
italic: false
|
||||
})
|
||||
const regularParagraph = buildParagraph(renderer, base)
|
||||
const boldParagraph = buildParagraph(renderer, { ...base, fontWeight: 700 })
|
||||
|
||||
expect(boldParagraph.getLongestLine()).toBeGreaterThan(regularParagraph.getLongestLine())
|
||||
regularParagraph.delete()
|
||||
boldParagraph.delete()
|
||||
surface.delete()
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderText headless visual', () => {
|
||||
test('detects base direction for Arabic and mixed text', () => {
|
||||
expect(detectTextDirection('مرحبا')).toBe('RTL')
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ describe('chooseLocalFontMatch', () => {
|
|||
expect(chooseLocalFontMatch(fonts, 'Inter', 'Regular')?.style).toBe('Regular')
|
||||
})
|
||||
|
||||
test('does not fall back from upright requests to italic when upright faces exist', () => {
|
||||
expect(chooseLocalFontMatch(fonts, 'Inter', 'Bold')?.style).toBe('Semi Bold')
|
||||
test('does not substitute nearby weights for explicit style requests', () => {
|
||||
expect(chooseLocalFontMatch(fonts, 'Inter', 'Bold')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue