28 lines
1.4 KiB
TypeScript
28 lines
1.4 KiB
TypeScript
// Run in the preview frame after fonts/pagination settle, rather than using text-cell tops.
|
|
export function minimumNameInkClearance(selector: string): number {
|
|
const name = document.querySelector(selector);
|
|
const flow = document.querySelector('[data-cv-preview-flow]');
|
|
const context = document.createElement('canvas').getContext('2d');
|
|
if (!name || !flow || !context) throw new Error('Missing name or settled print fragment');
|
|
const clipTop = flow.getBoundingClientRect().top;
|
|
const clearance: number[] = [];
|
|
const walker = document.createTreeWalker(name, NodeFilter.SHOW_TEXT);
|
|
let node;
|
|
while ((node = walker.nextNode())) {
|
|
const style = getComputedStyle(node.parentElement!);
|
|
context.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
|
|
let offset = 0;
|
|
for (const character of node.textContent || '') {
|
|
const range = document.createRange();
|
|
range.setStart(node, offset); range.setEnd(node, offset + character.length);
|
|
offset += character.length;
|
|
if (!character.trim()) continue;
|
|
const rect = range.getClientRects()[0], glyph = context.measureText(character);
|
|
if (!rect) throw new Error('Name glyph has no rendered position');
|
|
clearance.push(rect.top + glyph.fontBoundingBoxAscent - glyph.actualBoundingBoxAscent - clipTop);
|
|
}
|
|
}
|
|
if (!clearance.length) throw new Error('Empty name cannot qualify glyph clearance');
|
|
return Math.min(...clearance);
|
|
}
|