-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtext.js
81 lines (67 loc) · 2.69 KB
/
text.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// TODO text-decoration
import $ from '../utils/dom-render-svg'
const matchFont = s => ({ family, style = 'normal', weight = '400' } = {}) =>
family === (s.getPropertyValue('font-family') ?? '').replace(/['"]/g, '') &&
style === (s.getPropertyValue('font-style') ?? 'normal') &&
weight === (s.getPropertyValue('font-weight') ?? '400')
export default ({ debug, fonts }) => async (string, { x, y, width, height, style }, {
splitText = false
}) => {
if (!string) return
const g = $('g', { class: 'text-fragment' })
// Find font
const font = fonts.find(matchFont(style))
if (!font) throw new Error(`Cannot find font '${style.getPropertyValue('font-family')} ${style.getPropertyValue('font-style')} ${style.getPropertyValue('font-weight')}'`)
// Extract font metrics
const { unitsPerEm } = font.opentype
const ascender = font.opentype.tables.hhea.ascender
const descender = font.opentype.tables.hhea.descender
// Extract CSS props
const letterSpacing = style.getPropertyValue('letter-spacing')
const fontSize = parseFloat(style.getPropertyValue('font-size'))
// Compute metrics
const lineBox = (ascender - descender) / unitsPerEm
const leading = (fontSize * lineBox) - Math.abs(descender / unitsPerEm) * fontSize
// Render various metrics for debug
line('start', 0, { orientation: 'vertical', stroke: 'red' })
line('end', width, { orientation: 'vertical', stroke: 'red' })
line('leading', leading, { stroke: '#4b96ff' })
if (letterSpacing !== 'normal' || splitText) {
const ls = letterSpacing === 'normal' ? 0 : parseFloat(letterSpacing)
// Render letter by letter in case of non-default letter-spacing or explicit split
for (const c of string) {
if (!c.match(/\s/)) { // Do not render spaces
$('path', {
d: font.opentype.getPath(c, x, y + leading, fontSize).toPathData(3),
fill: style.getPropertyValue('color')
}, g)
}
x += font.opentype.getAdvanceWidth(c, fontSize) + ls
}
} else {
// Render string
$('path', {
d: font.opentype.getPath(string, x, y + leading, fontSize, {
features: {
// TODO extract from CSS props
liga: true,
rlig: true
}
}).toPathData(3),
fill: style.getPropertyValue('color')
}, g)
}
return g
function line (title, v, { orientation = 'horizontal', stroke = 'black' } = {}) {
return debug && $('line', {
title,
'data-value': v,
x1: orientation === 'horizontal' ? x : x + v,
x2: orientation === 'horizontal' ? x + width : x + v,
y1: orientation === 'horizontal' ? y + v : y,
y2: orientation === 'horizontal' ? y + v : y + height,
stroke,
class: 'debug'
}, g)
}
}