quote-api / test-metrics.js
Quote API Deploy
feat: deploy quote-api to Hugging Face Spaces
2413e0a unverified
Raw
History Blame Contribute Delete
4.81 kB
// Numeric assertions for the metric text layout: line geometry must be a
// constant of the font (em box), never of the glyph shapes drawn.
// Run: node test-metrics.js → exits non-zero on any failed assertion.
const assert = require('assert')
const { createCanvas } = require('canvas')
const { drawMultilineText } = require('./utils/quote-generate/text-renderer')
const { drawLabel } = require('./utils/quote-generate/canvas-utils')
const { leaf } = require('./utils/quote-generate/layout-box')
const { fontMetrics } = require('./utils/quote-generate/text-prepare')
const FS = 24
async function main () {
const { ascent, descent } = fontMetrics(FS)
const lineHeight = FS * 1.2
const W = 2000
const H = 2000
// 1. Glyph shapes must not change the canvas height (the old ink-trim bug):
// no ascenders/descenders vs full ascenders/descenders/diacritics.
const low = await drawMultilineText('осе ссс еео', [], FS, '#fff', 0, FS, W, H, 'apple', null)
const tall = await drawMultilineText('Іфj Ďjq Ції', [], FS, '#fff', 0, FS, W, H, 'apple', null)
assert.strictEqual(low.height, tall.height,
`single line: ${low.height} (low ink) != ${tall.height} (tall ink)`)
// 2. Single line height is exactly the font em box.
assert.strictEqual(low.height, Math.max(1, Math.ceil(ascent + descent)),
`single line height ${low.height} != ceil(ascent+descent) ${Math.ceil(ascent + descent)}`)
// 3. n-line text: height = (n-1)*lineHeight + ascent + descent, regardless of glyphs.
for (const n of [2, 3, 5]) {
const lowText = Array(n).fill('ооо').join('\n')
const tallText = Array(n).fill('Іфj').join('\n')
const a = await drawMultilineText(lowText, [], FS, '#fff', 0, FS, W, H, 'apple', null)
const b = await drawMultilineText(tallText, [], FS, '#fff', 0, FS, W, H, 'apple', null)
const expected = Math.ceil((n - 1) * lineHeight + ascent + descent)
assert.strictEqual(a.height, expected, `${n} lines (low): ${a.height} != ${expected}`)
assert.strictEqual(b.height, expected, `${n} lines (tall): ${b.height} != ${expected}`)
}
// 4. leaf() takes canvases at face value (no ink scanning) and drops 1×1 stubs.
const l = leaf(low)
assert.strictEqual(l.h, low.height, 'leaf height must equal canvas height')
assert.strictEqual(l.srcY, 0, 'leaf must not crop the top')
const stub = await drawMultilineText('', [], FS, '#fff', 0, FS, W, H, 'apple', null)
assert.strictEqual(leaf(stub), null, 'empty-text stub must resolve to null leaf')
// 5. drawLabel: same metric rule at label sizes, glyph-independent.
const em13 = fontMetrics(13)
const lab1 = drawLabel('ооо', 13, '#fff')
const lab2 = drawLabel('Іфj', 13, '#fff')
assert.strictEqual(lab1.height, lab2.height, 'label heights must match')
assert.strictEqual(lab1.height, Math.max(1, Math.ceil(em13.ascent + em13.descent)),
`label height ${lab1.height} != em box ${Math.ceil(em13.ascent + em13.descent)}`)
// 6. The line box covers everything that gets drawn into it (no clipping):
// emoji images span [baseline−0.85·fs, baseline+0.30·fs]; probe glyph ink
// must fit too. Checked directly against the measured extents.
const probeCtx = createCanvas(1, 1).getContext('2d')
probeCtx.font = `${FS}px NotoSans`
const inkAsc = probeCtx.measureText('ẤÅЇĎ').actualBoundingBoxAscent
const inkDesc = probeCtx.measureText('jqyḑộ').actualBoundingBoxDescent
assert.ok(ascent >= FS * 0.85, `ascent ${ascent} < emoji top ${FS * 0.85}`)
assert.ok(descent >= FS * 0.3, `descent ${descent} < emoji bottom ${FS * 0.3}`)
assert.ok(ascent >= inkAsc, `ascent ${ascent} < probe ink ascent ${inkAsc}`)
assert.ok(descent >= inkDesc, `descent ${descent} < probe ink descent ${inkDesc}`)
// 7. End-to-end: two messages with identical structure but different ink
// extents must produce pixel-identical bubble dimensions.
const QuoteGenerate = require('./utils/quote-generate')
const qg = new QuoteGenerate('0:x')
const mk = (text) => qg.generate('#1b1429', '#1b1429', {
from: { id: 42, name: 'Тест Тестенко' },
text,
avatar: false
}, 512, 512, 2, 'apple')
const qLow = await mk('осе ссс еео')
const qTall = await mk('Іфj Ďjq Ції')
assert.strictEqual(qLow.height, qTall.height,
`bubble heights differ: ${qLow.height} vs ${qTall.height}`)
console.log('OK: metric layout assertions passed')
console.log(` e2e bubble: ${qLow.width}x${qLow.height} == ${qTall.width}x${qTall.height}`)
console.log(` font em box @${FS}px: ascent=${ascent} descent=${descent} lineHeight=${lineHeight}`)
console.log(` 1 line=${low.height}px, n lines=(n-1)*${lineHeight}+${ascent}+${descent}`)
}
main().catch((err) => {
console.error('FAIL:', err.message)
process.exit(1)
})