UNPKG

string-to-svg

Version:

Convert text to SVG path without native dependence.

112 lines (110 loc) 3.3 kB
// src/index.ts function getD(text, options) { const fontSize = options.fontSize || 72; const kerning = options.kerning; const letterSpacing = options.letterSpacing; const tracking = options.tracking; const metrics = getMetrics(text, options); const path = options.font.getPath(text, metrics.x, metrics.baseline, fontSize, { kerning, letterSpacing, tracking }); return path.toPathData(2); } function getPath(text, options) { const attributes = options.attributes || {}; const attributesString = Object.keys(attributes).map((key) => `${key}="${attributes[key]}"`).join(" "); const d = getD(text, options); if (attributesString) { return `<path ${attributesString} d="${d}"/>`; } return `<path d="${d}"/>`; } function getHeight(fontSize, font) { const fontScale = 1 / font.unitsPerEm * fontSize; return (font.ascender - font.descender) * fontScale; } function getWidth(text, options) { const fontSize = options.fontSize || 72; const kerning = "kerning" in options ? options.kerning : true; const fontScale = 1 / options.font.unitsPerEm * fontSize; let width = 0; const glyphs = options.font.stringToGlyphs(text); for (let i = 0; i < glyphs.length; i++) { const glyph = glyphs[i]; if (glyph.advanceWidth) { width += glyph.advanceWidth * fontScale; } if (kerning && i < glyphs.length - 1) { const kerningValue = options.font.getKerningValue(glyph, glyphs[i + 1]); width += kerningValue * fontScale; } if (options.letterSpacing) { width += options.letterSpacing * fontSize; } else if (options.tracking) { width += options.tracking / 1e3 * fontSize; } } return width; } function parseAnchorOption(anchor) { let horizontalMatch = anchor.match(/left|center|right/gi) || []; let horizontal = horizontalMatch.length === 0 ? "left" : horizontalMatch[0]; let verticalMatch = anchor.match(/baseline|top|bottom|middle/gi) || []; let vertical = verticalMatch.length === 0 ? "baseline" : verticalMatch[0]; return { horizontal, vertical }; } function getMetrics(text, options) { const fontSize = options.fontSize || 72; const anchor = parseAnchorOption(options.anchor || "left baseline"); const width = getWidth(text, options); const height = getHeight(fontSize, options.font); const fontScale = 1 / options.font.unitsPerEm * fontSize; const ascender = options.font.ascender * fontScale; const descender = options.font.descender * fontScale; let x = options.x || 0; switch (anchor.horizontal) { case "left": x -= 0; break; case "center": x -= width / 2; break; case "right": x -= width; break; default: throw new Error(`Unknown anchor option: ${anchor.horizontal}`); } let y = options.y || 0; switch (anchor.vertical) { case "baseline": y -= ascender; break; case "top": y -= 0; break; case "middle": y -= height / 2; break; case "bottom": y -= height; break; default: throw new Error(`Unknown anchor option: ${anchor.vertical}`); } const baseline = y + ascender; return { x, y, baseline, width, height, ascender, descender }; } export { getD, getPath, getHeight, getWidth, getMetrics };