Optimize spacing variants' building
This commit is contained in:
parent
b7a59ee481
commit
12a4f1edb1
14 changed files with 775 additions and 372 deletions
|
@ -1,20 +1,22 @@
|
|||
import { buildGlyphs } from "../glyphs/index.mjs";
|
||||
import { copyFontMetrics } from "../meta/aesthetics.mjs";
|
||||
import { assignFontNames } from "../meta/naming.mjs";
|
||||
import { buildOtl } from "../otl/index.mjs";
|
||||
|
||||
import * as Caching from "./caching/index.mjs";
|
||||
import { CreateEmptyFont } from "./empty-font.mjs";
|
||||
import { finalizeFont } from "./finalize/index.mjs";
|
||||
import { CreateEmptyFont } from "./meta/empty-font.mjs";
|
||||
import { assignFontNames } from "./meta/naming.mjs";
|
||||
import { convertOtd } from "./otd-conv/index.mjs";
|
||||
|
||||
("use strict");
|
||||
export async function buildFont(argv, para) {
|
||||
const gs = buildGlyphs(para);
|
||||
const baseFont = CreateEmptyFont(argv);
|
||||
assignFontNames(para, baseFont);
|
||||
assignFontNames(baseFont, para.naming, para.isQuasiProportional);
|
||||
|
||||
const gs = buildGlyphs(para);
|
||||
copyFontMetrics(gs.fontMetrics, baseFont);
|
||||
|
||||
const otl = buildOtl(para, gs.glyphStore);
|
||||
|
||||
// Regulate
|
||||
const excludeChars = new Set();
|
||||
if (para.excludedCharRanges) {
|
||||
|
|
|
@ -15,9 +15,10 @@ function assignSubRank(glyphStore) {
|
|||
let sr = 0;
|
||||
for (const g of glyphStore.glyphs()) g.subRank = sr++;
|
||||
}
|
||||
// In FontConfig, a font is considered "monospace" if and only if all non-combining characters
|
||||
// (AW > 0) have the same width. We use this method to validate whether our "Fixed" subfamilies
|
||||
// are properly built.
|
||||
|
||||
// In FontConfig, a font is considered "monospace" if and only if all encoded non-combining
|
||||
// characters (AW > 0) have the same width. We use this method to validate whether our
|
||||
// "Fixed" subfamilies are properly built.
|
||||
function validateMonospace(para, glyphStore) {
|
||||
let awSet = new Set();
|
||||
for (const [u, g] of glyphStore.encodedEntries()) {
|
||||
|
|
241
font-src/gen/meta/naming.mjs
Normal file
241
font-src/gen/meta/naming.mjs
Normal file
|
@ -0,0 +1,241 @@
|
|||
import { Ot } from "ot-builder";
|
||||
import semver from "semver";
|
||||
|
||||
export function createNamingDictFromArgv(argv) {
|
||||
return {
|
||||
family: argv.menu.family,
|
||||
version: argv.menu.version,
|
||||
weight: argv.menu.weight - 0,
|
||||
width: argv.menu.width - 0,
|
||||
slope: argv.menu.slope
|
||||
};
|
||||
}
|
||||
|
||||
export function assignFontNames(font, naming, isQuasiProportional) {
|
||||
// Clear existing names
|
||||
font.name.records.length = 0;
|
||||
|
||||
// Preferred names
|
||||
const family = naming.family.trim();
|
||||
const style = getStyle(naming.weight, naming.width, naming.slope);
|
||||
|
||||
nameFont(font, Ot.Name.NameID.PreferredFamily, family);
|
||||
nameFont(font, Ot.Name.NameID.PreferredSubfamily, style);
|
||||
nameFont(font, Ot.Name.NameID.WwsFamily, family);
|
||||
nameFont(font, Ot.Name.NameID.WwsSubfamily, style);
|
||||
|
||||
// Compat banes
|
||||
const compat = getStyleLinkedStyles(naming.weight, naming.width, naming.slope);
|
||||
let compatFamily = family;
|
||||
if (compat.familySuffix !== " Regular") compatFamily = family + " " + compat.familySuffix;
|
||||
if (compatFamily.length >= 31) compatFamily = family + " " + compat.familySuffixShort;
|
||||
|
||||
nameFont(font, Ot.Name.NameID.LegacyFamily, compatFamily);
|
||||
nameFont(font, Ot.Name.NameID.LegacySubfamily, compat.style);
|
||||
|
||||
// Unique and Full name
|
||||
const uniqueName = `${family} ${style} ${naming.version}`;
|
||||
const fullName = style !== "Regular" ? `${family} ${style}` : family;
|
||||
const postscriptName = fullName.replace(/ /g, "-");
|
||||
nameFont(font, Ot.Name.NameID.UniqueFontId, uniqueName);
|
||||
nameFont(font, Ot.Name.NameID.FullFontName, fullName);
|
||||
nameFont(font, Ot.Name.NameID.PostscriptName, postscriptName);
|
||||
|
||||
// Weight, width and slope numbers
|
||||
font.os2.usWeightClass = naming.weight;
|
||||
font.os2.usWidthClass = naming.width;
|
||||
font.os2.panose.bWeight = 1 + naming.weight / 100;
|
||||
font.os2.sFamilyClass = 0x809;
|
||||
|
||||
// HEAD and OS/2 flags
|
||||
const isItalic = naming.slope === "italic";
|
||||
const isOblique = naming.slope === "oblique";
|
||||
const isBold = naming.weight > 650;
|
||||
|
||||
// prettier-ignore
|
||||
font.os2.fsSelection = accumulateFlags(
|
||||
[Ot.Os2.FsSelection.OBLIQUE, isOblique],
|
||||
[Ot.Os2.FsSelection.BOLD, isBold],
|
||||
[Ot.Os2.FsSelection.ITALIC, isItalic || isOblique],
|
||||
[Ot.Os2.FsSelection.REGULAR, !isBold && !isItalic && !isOblique],
|
||||
[Ot.Os2.FsSelection.USE_TYPO_METRICS, true]
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
font.head.macStyle = accumulateFlags(
|
||||
[Ot.Head.MacStyle.Bold, isBold],
|
||||
[Ot.Head.MacStyle.Italic, isItalic || isOblique],
|
||||
[Ot.Head.MacStyle.Condensed, naming.width < 5],
|
||||
[Ot.Head.MacStyle.Extended, naming.width > 5]
|
||||
);
|
||||
|
||||
// Panose
|
||||
font.os2.panose.bFamilyType = 2;
|
||||
font.os2.panose.bContrast = 3;
|
||||
font.os2.panose.bXHeight = 4;
|
||||
|
||||
// Pitch
|
||||
if (!isQuasiProportional) {
|
||||
font.os2.panose.bProportion = 9; // Monospaced
|
||||
font.post.isFixedPitch = true;
|
||||
} else {
|
||||
font.os2.panose.bProportion = 0;
|
||||
font.post.isFixedPitch = false;
|
||||
}
|
||||
|
||||
// Licensing
|
||||
if (naming.miscNames) {
|
||||
nameFont(font, Ot.Name.NameID.Copyright, naming.miscNames.copyright);
|
||||
nameFont(font, Ot.Name.NameID.Manufacturer, naming.miscNames.manufacturer);
|
||||
nameFont(font, Ot.Name.NameID.Designer, naming.miscNames.designer);
|
||||
nameFont(font, Ot.Name.NameID.Description, naming.miscNames.description);
|
||||
nameFont(font, Ot.Name.NameID.LicenseDescription, naming.miscNames.licence);
|
||||
}
|
||||
|
||||
// Version
|
||||
nameFont(font, Ot.Name.NameID.VersionString, `Version ${naming.version}`);
|
||||
const majorVersion = semver.major(naming.version);
|
||||
const minorVersion = semver.minor(naming.version);
|
||||
const patchVersion = semver.patch(naming.version);
|
||||
if (minorVersion > 99 || patchVersion > 99) throw new RangeError("Version number overflow");
|
||||
font.head.fontRevision = majorVersion + (minorVersion * 10 + patchVersion) / 1000;
|
||||
|
||||
// Misc
|
||||
font.os2.ulCodePageRange1 = 0x2000011f;
|
||||
font.os2.ulCodePageRange2 = 0xc4000000;
|
||||
font.head.flags = accumulateFlags(
|
||||
[Ot.Head.Flags.BaseLineYAt0, true],
|
||||
[Ot.Head.Flags.LeftSidebearingAtX0, true],
|
||||
[Ot.Head.Flags.InstructionsMayDependOnPointSize, true],
|
||||
[Ot.Head.Flags.ForcePpemToBeInteger, true],
|
||||
[Ot.Head.Flags.InstructionMayAlterAdvanceWidth, true]
|
||||
);
|
||||
}
|
||||
|
||||
function accumulateFlags(...entries) {
|
||||
let s = 0;
|
||||
for (const [flag, cond] of entries) {
|
||||
if (cond) s |= flag;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function getStyleLinkedStyles(weight, width, slope) {
|
||||
let linkWeight = weight;
|
||||
let linkSlope = slope;
|
||||
let nameSuffixWeight = 400;
|
||||
let nameSuffixWidth = width;
|
||||
let nameSuffixSlope = "normal";
|
||||
|
||||
if (!(linkWeight === 400 || linkWeight == 700)) {
|
||||
nameSuffixWeight = linkWeight;
|
||||
linkWeight = 400;
|
||||
}
|
||||
|
||||
if (!(linkSlope === "normal" || linkSlope === "italic")) {
|
||||
nameSuffixSlope = linkSlope;
|
||||
linkSlope = "normal";
|
||||
}
|
||||
|
||||
return {
|
||||
style: getStyle(linkWeight, 5, linkSlope),
|
||||
familySuffix: getStyle(nameSuffixWeight, nameSuffixWidth, nameSuffixSlope),
|
||||
familySuffixShort: getShortStyle(nameSuffixWeight, nameSuffixWidth, nameSuffixSlope)
|
||||
};
|
||||
}
|
||||
|
||||
function nameFont(font, nameID, str) {
|
||||
// Mac Roman
|
||||
font.name.records.push({
|
||||
platformID: 1,
|
||||
encodingID: 0,
|
||||
languageID: 0,
|
||||
nameID,
|
||||
value: Buffer.from(str, "utf-8")
|
||||
});
|
||||
|
||||
// Windows Unicode English
|
||||
font.name.records.push({
|
||||
platformID: 3,
|
||||
encodingID: 1,
|
||||
languageID: 1033,
|
||||
nameID,
|
||||
value: str
|
||||
});
|
||||
}
|
||||
|
||||
function isRBIZ(weight, slope) {
|
||||
return (weight === 400 || weight === 700) && (slope === "normal" || slope === "italic");
|
||||
}
|
||||
|
||||
function getStyle(weight, width, slope) {
|
||||
const weightPart = weightToMenuStyleMap[weight] ?? "W" + weight;
|
||||
const widthPart = widthToMenuStyleMap[width] ?? "Wd" + width;
|
||||
const slopePart = slopeToMenuStyleMap[slope] ?? "";
|
||||
const rawName = weightPart + " " + widthPart + " " + slopePart;
|
||||
return rawName.replace(/ +/g, " ").trim() || "Regular";
|
||||
}
|
||||
function getShortStyle(weight, width, slope) {
|
||||
const weightPart = weightToMenuStyleMapShort[weight] ?? "W" + weight;
|
||||
const widthPart = widthToMenuStyleMapShort[width] ?? "Wd" + width;
|
||||
const slopePart = slopeToMenuStyleMapShort[slope] ?? "";
|
||||
const rawName = weightPart + " " + widthPart + " " + slopePart;
|
||||
return rawName.replace(/ +/g, " ").trim() || "Regular";
|
||||
}
|
||||
|
||||
const weightToMenuStyleMap = {
|
||||
100: "Thin",
|
||||
200: "Extralight",
|
||||
300: "Light",
|
||||
400: "",
|
||||
450: "Book",
|
||||
500: "Medium",
|
||||
600: "Semibold",
|
||||
700: "Bold",
|
||||
800: "Extrabold",
|
||||
900: "Heavy"
|
||||
};
|
||||
const widthToMenuStyleMap = {
|
||||
1: "Ultra-Condensed",
|
||||
2: "Extra-Condensed",
|
||||
3: "Condensed",
|
||||
4: "Semi-Condensed",
|
||||
5: "",
|
||||
6: "Semi-Extended",
|
||||
7: "Extended",
|
||||
8: "Extra-Extended",
|
||||
9: "Ultra-Extended"
|
||||
};
|
||||
const slopeToMenuStyleMap = {
|
||||
normal: "",
|
||||
italic: "Italic",
|
||||
oblique: "Oblique"
|
||||
};
|
||||
const weightToMenuStyleMapShort = {
|
||||
100: "Th",
|
||||
200: "XLt",
|
||||
300: "Lt",
|
||||
400: "",
|
||||
450: "Bk",
|
||||
500: "Md",
|
||||
600: "SmBd",
|
||||
700: "Bd",
|
||||
800: "XBd",
|
||||
900: "Hv"
|
||||
};
|
||||
const widthToMenuStyleMapShort = {
|
||||
1: "UltCn",
|
||||
2: "XCn",
|
||||
3: "Cn",
|
||||
4: "SmCn",
|
||||
5: "",
|
||||
6: "SmEx",
|
||||
7: "Ex",
|
||||
8: "XEx",
|
||||
9: "UltEx"
|
||||
};
|
||||
const slopeToMenuStyleMapShort = {
|
||||
normal: "",
|
||||
italic: "It",
|
||||
oblique: "Obl"
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue