const Glyph = require("./glyph"); const autoRef = require("./autoref"); const caryllShapeOps = require("caryll-shapeops"); const c2q = require("megaminx").geometry.c2q; function regulateGlyph(g, skew) { if (!g.contours) return; // Regulate for (let k = 0; k < g.contours.length; k++) { const contour = g.contours[k]; for (let p = 0; p < contour.length; p++) { contour[p].x += contour[p].y * skew; if (!contour[p].on) continue; contour[p].x = Math.round(contour[p].x); } let offJ = null, mx = null; for (let p = 0; p < contour.length; p++) { if (!contour[p].on) continue; if (offJ) { const origX = contour[p].x; const rx = Math.round(contour[p].x * 4) / 4; const origX0 = mx; const rx0 = contour[offJ - 1].x; if (origX === origX0) continue; for (let pOff = offJ; pOff < p; pOff++) { contour[pOff].x = ((contour[pOff].x - origX0) / (origX - origX0)) * (rx - rx0) + rx0; } } mx = contour[p].x; contour[p].x = Math.round(contour[p].x * 4) / 4; offJ = p + 1; } } const cSimple = []; const cFill = []; for (let k = 0; k < g.contours.length; k++) { const contour = g.contours[k]; if (contour.length <= 2) cSimple.push(Glyph.contourToStandardCubic(contour)); else cFill.push(Glyph.contourToStandardCubic(contour)); } // De-overlap g.contours = [...cSimple, ...caryllShapeOps.removeOverlap(cFill, 1, 2048, true)]; // g.contours = [...cSimple, ...cFill]; // Finalize g.contours = c2q.contours(g.contours); for (let k = 0; k < g.contours.length; k++) { const contour = g.contours[k]; for (let p = 0; p < contour.length; p++) { contour[p].x -= contour[p].y * skew; } } } function byGlyphPriority(a, b) { const pri1 = a.cmpPriority || 0; const pri2 = b.cmpPriority || 0; if (pri1 > pri2) return -1; if (pri1 < pri2) return 1; if (a.contours && b.contours && a.contours.length < b.contours.length) return 1; if (a.contours && b.contours && a.contours.length > b.contours.length) return -1; if (a.advanceWidth < b.advanceWidth) return -1; if (a.advanceWidth > b.advanceWidth) return 1; if ((a.unicode && a.unicode[0] && !b.unicode) || !b.unicode[0]) return -1; if ((b.unicode && b.unicode[0] && !a.unicode) || !a.unicode[0]) return +1; if (a.unicode && a.unicode[0] && b.unicode && b.unicode[0] && a.unicode[0] < b.unicode[0]) return -1; if (a.unicode && a.unicode[0] && b.unicode && b.unicode[0] && a.unicode[0] > b.unicode[0]) return +1; return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; } module.exports = function(font, skew) { const excludeUnicode = new Set(); excludeUnicode.add(0x80); for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c); // autoref font.glyf = font.glyf.map((g, j) => ((g.glyphOrder = j), g)).sort(byGlyphPriority); autoRef(font.glyf, excludeUnicode); // regulate for (let g of font.glyf) regulateGlyph(g, skew); // reorder font.glyf = font.glyf.sort((a, b) => a.glyphOrder - b.glyphOrder); };