116 lines
3.2 KiB
JavaScript
116 lines
3.2 KiB
JavaScript
"use strict";
|
|
|
|
const autoRef = require("./autoref");
|
|
const caryllShapeOps = require("caryll-shapeops");
|
|
const curveUtil = require("../../support/curve-util");
|
|
const { fairifyQuad } = require("../../support/fairify");
|
|
const Transform = require("../../support/transform");
|
|
const { AnyCv } = require("../../support/gr");
|
|
const gcFont = require("./gc");
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
g.contours = simplifyContours(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 simplifyContours(contours) {
|
|
const gizmo = Transform.Id();
|
|
const source = [];
|
|
for (const contour of contours) {
|
|
if (contour.length > 2) source.push(curveUtil.convertContourToCubic(contour));
|
|
}
|
|
const simplified = caryllShapeOps.removeOverlap(source, 1, 1 << 17, true);
|
|
|
|
const result = [];
|
|
for (const contour of simplified) {
|
|
if (contour.length <= 2) continue;
|
|
result.push(curveUtil.cleanupQuadContour(fairifyQuad(contour, gizmo)));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function byGlyphPriority(a, b) {
|
|
const pri1 = a.autoRefPriority || 0;
|
|
const pri2 = b.autoRefPriority || 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;
|
|
return 0;
|
|
}
|
|
|
|
function byRank(a, b) {
|
|
return (b.glyphRank || 0) - (a.glyphRank || 0) || (a.glyphOrder || 0) - (b.glyphOrder || 0);
|
|
}
|
|
|
|
function regulateGlyphList(para, gs) {
|
|
const skew = Math.tan(((para.italicAngle || 0) / 180) * Math.PI);
|
|
|
|
const excludeUnicode = new Set();
|
|
excludeUnicode.add(0x80);
|
|
for (let c = 0x2500; c <= 0x259f; c++) excludeUnicode.add(c);
|
|
|
|
// autoref
|
|
for (let j = 0; j < gs.length; j++) {
|
|
gs[j].glyphOrder = j;
|
|
if (AnyCv.query(gs[j]).length) gs[j].autoRefPriority = -1;
|
|
if (gs[j].unicode) {
|
|
for (const u of gs[j].unicode) {
|
|
if (excludeUnicode.has(u)) gs[j].avoidBeingComposite = true;
|
|
}
|
|
}
|
|
}
|
|
gs.sort(byGlyphPriority);
|
|
autoRef(gs, excludeUnicode);
|
|
|
|
// regulate
|
|
for (let g of gs) regulateGlyph(g, skew);
|
|
|
|
// reorder
|
|
return gs.sort(byRank);
|
|
}
|
|
|
|
function filterWideGlyphs(para, glyphList) {
|
|
if (para.forceMonospace && para.spacing == 0) {
|
|
for (const g of glyphList) g.advanceWidth = Math.round(g.advanceWidth || 0);
|
|
return glyphList.filter(g => !(g.advanceWidth > Math.round(para.width)));
|
|
}
|
|
return glyphList;
|
|
}
|
|
|
|
function extractGlyfCmap(glyphList, font) {
|
|
const glyf = {};
|
|
const cmap = {};
|
|
for (let g of glyphList) {
|
|
glyf[g.name] = g;
|
|
|
|
if (!g.unicode) continue;
|
|
for (let u of g.unicode) {
|
|
if (isFinite(u - 0)) cmap[u] = g.name;
|
|
}
|
|
}
|
|
font.glyf = glyf;
|
|
font.cmap = cmap;
|
|
}
|
|
|
|
module.exports = function finalizeFont(para, rawGlyphList, excludedCodePoints, font) {
|
|
const glyphList = filterWideGlyphs(para, rawGlyphList);
|
|
gcFont(glyphList, excludedCodePoints, font, {});
|
|
extractGlyfCmap(regulateGlyphList(para, glyphList), font);
|
|
};
|