string-to-svg
Version:
Convert text to SVG path without native dependence.
169 lines (165 loc) • 5.25 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/node.ts
var node_exports = {};
__export(node_exports, {
Font: () => import_opentype.Font,
getD: () => getD,
getHeight: () => getHeight,
getMetrics: () => getMetrics,
getPath: () => getPath,
getSVG: () => getSVG,
getWidth: () => getWidth,
loadFont: () => loadFont
});
module.exports = __toCommonJS(node_exports);
var import_path = require("path");
var import_opentype = require("opentype.js");
// 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
};
}
// src/node.ts
function loadFont(fontPath) {
const path = (0, import_path.join)(__dirname, fontPath);
return (0, import_opentype.loadSync)(path);
}
function getSVG(text, options) {
if (!options.font) {
options.font = loadFont("../fonts/ipag.ttf");
}
const metrics = getMetrics(text, options);
let svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${metrics.width}" height="${metrics.height}">`;
if (options.beforePath)
svg += options.beforePath;
svg += getPath(text, options);
if (options.afterPath)
svg += options.afterPath;
svg += "</svg>";
return svg;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Font,
getD,
getHeight,
getMetrics,
getPath,
getSVG,
getWidth,
loadFont
});