import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; import opentype from "opentype.js"; import { glyphA, glypha } from "../src/glyphs/A.js"; import { glyphB, glyphb } from "../src/glyphs/B.js"; import { glyphC, glyphc } from "../src/glyphs/C.js"; import { glyphD, glyphd } from "../src/glyphs/D.js"; import { glyphE, glyphe } from "../src/glyphs/E.js"; import { glyphF, glyphf } from "../src/glyphs/F.js"; import { glyphG, glyphg } from "../src/glyphs/G.js"; import { glyphH, glyphh } from "../src/glyphs/H.js"; import { glyphI, glyphi } from "../src/glyphs/I.js"; import { glyphJ, glyphj } from "../src/glyphs/J.js"; import { glyphK, glyphk } from "../src/glyphs/K.js"; import { glyphL, glyphl } from "../src/glyphs/L.js"; import { glyphM, glyphm } from "../src/glyphs/M.js"; import { glyphN, glyphn } from "../src/glyphs/N.js"; import { glyphO, glypho } from "../src/glyphs/O.js"; import { glyphP, glyphp } from "../src/glyphs/P.js"; import { glyphQ, glyphq } from "../src/glyphs/Q.js"; import { glyphR, glyphr } from "../src/glyphs/R.js"; import { glyphS, glyphs as glyphsLowerS } from "../src/glyphs/S.js"; import { glyphT, glypht } from "../src/glyphs/T.js"; import { glyphU, glyphu } from "../src/glyphs/U.js"; import { glyphV, glyphv } from "../src/glyphs/V.js"; import { glyphW, glyphw } from "../src/glyphs/W.js"; import { glyphX, glyphx } from "../src/glyphs/X.js"; import { glyphY, glypthy as glyphy } from "../src/glyphs/Y.js"; import { glyphZ, glyphz } from "../src/glyphs/Z.js"; const __filename=fileURLToPath(import.meta.url); const __dirname=path.dirname(__filename); const outDir=path.join(__dirname,"..","dist"); if(!fs.existsSync(outDir))fs.mkdirSync(outDir,{recursive:true}); const familyName="Stain"; const styleName="Regular"; const unitsPerEm=1000; const ascender=800; const descender=-200; const defaultAdv=580; // slightly tighter default const wideAdv=700; // for M / W / m / w const spaceAdv=240; // slightly tighter space const periodAdv=220; // slightly tighter period const mkGlyph=(name,unicode,adv,pathFn)=> new opentype.Glyph({ name, unicode, advanceWidth:adv, path:pathFn(new opentype.Path()) }); const glyphSpace=mkGlyph("space"," ".codePointAt(0),spaceAdv,p=>p); const glyphPeriod=mkGlyph( "period", ".".codePointAt(0), periodAdv, p=>{ const x=80,w=70,h=120; p.moveTo(x,0); p.lineTo(x, h); p.lineTo(x+w, h); p.lineTo(x+w,0); p.close(); return p; } ); const glyphList=[ glyphSpace, glyphPeriod, glyphA(opentype,defaultAdv), glypha(opentype,defaultAdv), glyphB(opentype,defaultAdv), glyphb(opentype,defaultAdv), glyphC(opentype,defaultAdv), glyphc(opentype,defaultAdv), glyphD(opentype,defaultAdv), glyphd(opentype,defaultAdv), glyphE(opentype,defaultAdv), glyphe(opentype,defaultAdv), glyphF(opentype,defaultAdv), glyphf(opentype,defaultAdv), glyphG(opentype,defaultAdv), glyphg(opentype,defaultAdv), glyphH(opentype,defaultAdv), glyphh(opentype,defaultAdv), glyphI(opentype,defaultAdv), glyphi(opentype,defaultAdv), glyphJ(opentype,defaultAdv), glyphj(opentype,defaultAdv), glyphK(opentype,defaultAdv), glyphk(opentype,defaultAdv), glyphL(opentype,defaultAdv), glyphl(opentype,defaultAdv), glyphM(opentype,wideAdv), glyphm(opentype,wideAdv), glyphN(opentype,defaultAdv), glyphn(opentype,defaultAdv), glyphO(opentype,defaultAdv), glypho(opentype,defaultAdv), glyphP(opentype,defaultAdv), glyphp(opentype,defaultAdv), glyphQ(opentype,defaultAdv), glyphq(opentype,defaultAdv), glyphR(opentype,defaultAdv), glyphr(opentype,defaultAdv), glyphS(opentype,defaultAdv), glyphsLowerS(opentype,defaultAdv), glyphT(opentype,defaultAdv), glypht(opentype,defaultAdv), glyphU(opentype,defaultAdv), glyphu(opentype,defaultAdv), glyphV(opentype,defaultAdv), glyphv(opentype,defaultAdv), glyphW(opentype,wideAdv), glyphw(opentype,wideAdv-40), glyphX(opentype,defaultAdv), glyphx(opentype,defaultAdv), glyphY(opentype,defaultAdv), glyphy(opentype,defaultAdv), glyphZ(opentype,defaultAdv), glyphz(opentype,defaultAdv) ]; const font=new opentype.Font({ familyName, styleName, unitsPerEm, ascender, descender, glyphs:glyphList, copyright:"Author: Anon. License: CC0 1.0 Universal." }); // basic kerning pairs to reduce obvious gaps const kernPairs=[ ["A","V",-70], ["A","W",-70], ["A","Y",-70], ["A","T",-50], ["A","O",-25], ["A","Q",-25], ["V","A",-70], ["V","O",-40], ["V","a",-50], ["V","o",-50], ["V","e",-45], ["V","u",-40], ["W","A",-70], ["W","O",-40], ["W","a",-50], ["W","o",-50], ["W","e",-45], ["W","u",-40], ["Y","A",-80], ["Y","O",-40], ["Y","a",-70], ["Y","o",-70], ["Y","e",-65], ["Y","u",-55], ["T","A",-60], ["T","O",-40], ["T","a",-70], ["T","o",-70], ["T","e",-65], ["T","u",-60], ["T","y",-50], ["L","T",-50], ["L","V",-70], ["L","W",-70], ["L","Y",-80], ["F","a",-45], ["F","o",-45], ["F","e",-40], ["P","A",-70], ["P","a",-40], ["P","o",-40], ["K","O",-40], ["K","a",-40], ["K","o",-40], ["K","e",-35], ["K","u",-35], [" ","A",-20], [" ","V",-15], [" ","W",-15], [" ","Y",-15] ]; const glyphIndexByName=name=>{ const g=font.glyphs.glyphs.find(gl=>gl.name===name); return g?g.index:0; }; font.kerningPairs=kernPairs.reduce((acc,[l,r,v])=>{ const left=glyphIndexByName(l); const right=glyphIndexByName(r); if(left && right) acc[`${left},${right}`]=v; return acc; },{}); const buf=Buffer.from(font.toArrayBuffer()); const outPath=path.join(outDir,"Stain.otf"); fs.writeFileSync(outPath,buf); console.log(`Built ${outPath}`);