diff --git a/scripts/build-font.js b/scripts/build-font.js deleted file mode 100644 index 9a52998..0000000 --- a/scripts/build-font.js +++ /dev/null @@ -1,290 +0,0 @@ -import fs from "fs"; -import path from "path"; -import url from "url"; -import opentype from "opentype.js"; - -const __filename = url.fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const distDir = path.join(__dirname, "..", "dist"); -if (!fs.existsSync(distDir)) fs.mkdirSync(distDir, { recursive: true }); - -const UPM = 1000; -const ASC = 750; -const DSC = -250; -const STROKE = 72; -const CURVE = 0.44; -const LIGHT = 0.085; -const ANG = 9 * Math.PI / 180; - -const makeGlyph = ({ - name, - unicode, - advanceWidth, - path: makePath -}) => new opentype.Glyph({ - name, - unicode, - advanceWidth, - path: makePath() -}); - -const slant = x => x + Math.tan(ANG) * 0.12 * UPM; -const mix = (a, b, t) => a + (b - a) * t; -const curvePt = (x1, y1, x2, y2, t) => ({ - x: mix(x1, x2, t), - y: mix(y1, y2, t) -}); - -const baseMetrics = { - ascender: ASC, - descender: DSC, - unitsPerEm: UPM -}; - -const stainCommon = { - familyName: "Stain", - styleName: "Regular", - ...baseMetrics -}; - -const pA = () => { - const p = new opentype.Path(); - const w = 640; - const barH = 320; - const apexX = slant(120); - const apexY = ASC; - const leftBaseX = slant(80); - const rightBaseX = slant(w - 40); - const baseY = DSC * LIGHT; - p.moveTo(rightBaseX, baseY); - p.lineTo(rightBaseX - STROKE * 0.32, baseY); - p.lineTo(apexX + 36, apexY); - p.quadraticCurveTo(apexX, apexY - 18, apexX - 36, apexY); - p.lineTo(leftBaseX + STROKE * 0.28, baseY); - p.lineTo(leftBaseX, baseY); - p.close(); - const ib = 0.35; - const iLeft = mix(leftBaseX, rightBaseX, ib); - const iRight = mix(leftBaseX, rightBaseX, ib + 0.36); - const iTop = barH + 22; - const iBot = barH - 22; - p.moveTo(iLeft, iTop); - p.lineTo(iRight, iTop); - p.lineTo(iRight, iBot); - p.lineTo(iLeft, iBot); - p.close(); - return p; -}; - -const pB = () => { - const p = new opentype.Path(); - const xL = slant(80); - const xStemR = xL + STROKE * 0.9; - const yTop = ASC; - const yBot = DSC * LIGHT; - const midY = (yTop + yBot) / 2 + 40; - const o = 120; - const w = 640; - const cxR = slant(w - 40); - p.moveTo(xStemR, yBot); - p.lineTo(xL, yBot); - p.lineTo(xL, yTop); - p.lineTo(xStemR, yTop); - p.close(); - p.moveTo(xStemR, yTop); - p.curveTo( - mix(xStemR, cxR, CURVE), yTop, - cxR, mix(yTop, midY + 26, CURVE), - cxR, midY + 26 - ); - p.curveTo( - cxR, mix(midY + 26, midY - 8, CURVE), - mix(xStemR + o, cxR, CURVE), midY - 8, - xStemR + o, midY - 8 - ); - p.lineTo(xStemR, midY - 8); - p.close(); - p.moveTo(xStemR, midY - 26); - p.curveTo( - mix(xStemR, cxR, CURVE), midY - 26, - cxR, mix(midY - 26, yBot + 40, CURVE), - cxR, yBot + 40 - ); - p.curveTo( - cxR, mix(yBot + 40, yBot + 8, CURVE), - mix(xStemR + o, cxR, CURVE), yBot + 8, - xStemR + o, yBot + 8 - ); - p.lineTo(xStemR, yBot + 8); - p.close(); - return p; -}; - -const pC = () => { - const p = new opentype.Path(); - const cx = slant(360); - const cy = ASC * 0.55; - const rx = 290; - const ry = 260; - const i = 0.56; - const rxI = rx * i; - const ryI = ry * i; - const startAng = Math.PI * 0.22; - const endAng = Math.PI * 1.78; - const arc = (R, rY, rev) => { - const dir = rev ? -1 : 1; - const steps = 6; - for (let i = 0; i <= steps; i++) { - const t = i / steps; - const a = mix(startAng, endAng, t); - const x = cx + Math.cos(a) * R; - const y = cy + Math.sin(a) * rY; - if (i === 0 && !rev) p.moveTo(x, y); - else p.lineTo(x, y); - } - }; - arc(rx, ry, false); - arc(rxI, ryI, true); - p.close(); - const cutW = 80; - const cutX = cx + rx * 0.76; - const cutY1 = cy + 40; - const cutY2 = cy - 40; - p.moveTo(cutX, cutY1); - p.lineTo(cutX + cutW, cutY1 + 16); - p.lineTo(cutX + cutW, cutY2 - 16); - p.lineTo(cutX, cutY2); - p.close(); - return p; -}; - -const pa = () => { - const p = new opentype.Path(); - const x = slant(120); - const yBase = 0; - const h = 500; - const rx = 180; - const ry = 210; - const cx = slant(260); - const cy = h * 0.54; - const steps = 5; - for (let i = 0; i <= steps; i++) { - const t = i / steps; - const a = Math.PI * (0.15 + 1.85 * t); - const xx = cx + Math.cos(a) * rx; - const yy = cy + Math.sin(a) * ry; - if (!i) p.moveTo(xx, yy); - else p.lineTo(xx, yy); - } - const rxI = rx * 0.56; - const ryI = ry * 0.56; - for (let i = steps; i >= 0; i--) { - const t = i / steps; - const a = Math.PI * (0.18 + 1.6 * t); - const xx = cx + Math.cos(a) * rxI; - const yy = cy + Math.sin(a) * ryI; - p.lineTo(xx, yy); - } - p.close(); - const stemX = slant(360); - const top = h + 14; - p.moveTo(stemX, yBase - 10); - p.lineTo(stemX + STROKE * 0.7, yBase); - p.lineTo(stemX + STROKE * 0.7, top); - p.lineTo(stemX, top - 14); - p.close(); - return p; -}; - -const pb = () => { - const p = new opentype.Path(); - const x = slant(120); - const yBase = 0; - const top = ASC; - p.moveTo(x, yBase - 10); - p.lineTo(x + STROKE * 0.8, yBase); - p.lineTo(x + STROKE * 0.8, top); - p.lineTo(x, top - 16); - p.close(); - const cx = slant(340); - const cy = 360; - const rx = 180; - const ry = 210; - const steps = 6; - for (let i = 0; i <= steps; i++) { - const t = i / steps; - const a = Math.PI * (0.1 + 1.9 * t); - const xx = cx + Math.cos(a) * rx; - const yy = cy + Math.sin(a) * ry; - if (!i) p.moveTo(xx, yy); - else p.lineTo(xx, yy); - } - const rxI = rx * 0.55; - const ryI = ry * 0.55; - for (let i = steps; i >= 0; i--) { - const t = i / steps; - const a = Math.PI * (0.2 + 1.6 * t); - const xx = cx + Math.cos(a) * rxI; - const yy = cy + Math.sin(a) * ryI; - p.lineTo(xx, yy); - } - p.close(); - return p; -}; - -const pc = () => { - const p = new opentype.Path(); - const cx = slant(260); - const cy = 320; - const rx = 210; - const ry = 210; - const rxI = rx * 0.56; - const ryI = ry * 0.56; - const steps = 6; - const s = Math.PI * 0.18; - const e = Math.PI * 1.82; - for (let i = 0; i <= steps; i++) { - const t = i / steps; - const a = mix(s, e, t); - const x = cx + Math.cos(a) * rx; - const y = cy + Math.sin(a) * ry; - if (!i) p.moveTo(x, y); - else p.lineTo(x, y); - } - for (let i = steps; i >= 0; i--) { - const t = i / steps; - const a = mix(s + 0.08, e - 0.22, t); - const x = cx + Math.cos(a) * rxI; - const y = cy + Math.sin(a) * ryI; - p.lineTo(x, y); - } - p.close(); - return p; -}; - -const glyphs = [ - new opentype.Glyph({ name: ".notdef", advanceWidth: 600, path: new opentype.Path() }), - new opentype.Glyph({ name: "space", unicode: 32, advanceWidth: 260, path: new opentype.Path() }), - makeGlyph({ name: "A", unicode: 65, advanceWidth: 640, path: pA }), - makeGlyph({ name: "B", unicode: 66, advanceWidth: 640, path: pB }), - makeGlyph({ name: "C", unicode: 67, advanceWidth: 640, path: pC }), - makeGlyph({ name: "a", unicode: 97, advanceWidth: 540, path: pa }), - makeGlyph({ name: "b", unicode: 98, advanceWidth: 540, path: pb }), - makeGlyph({ name: "c", unicode: 99, advanceWidth: 520, path: pc }) -]; - -const font = new opentype.Font({ - ...stainCommon, - glyphs, - version: "0.3", - designer: "multipleof4", - license: "OFL-1.1", - manufacturer: "multipleof4", - description: "Stain: a Candara-inspired humanist sans prototype.", - trademark: "Stain" -}); - -const outPath = path.join(distDir, "Stain-Regular.ttf"); -const buf = Buffer.from(font.toArrayBuffer()); -fs.writeFileSync(outPath, buf); -console.log("Built", outPath);