From 25ee0bcc507136cf46089cc57669d34c1fa4fd5b Mon Sep 17 00:00:00 2001 From: be5invis Date: Sun, 27 Aug 2023 21:48:13 -0700 Subject: [PATCH] Generate TTFAutohint control files for better glyph display for variant glyphs (#1963). --- changes/26.2.3.md | 1 + font-src/gen/build-font.mjs | 6 +- font-src/gen/otd-conv/glyphs.mjs | 4 +- font-src/gen/ttfa-controls/index.mjs | 56 +++ font-src/generated/ttfa-ranges.mjs | 514 +++++++++++++++++++++++++++ font-src/index.mjs | 5 +- font-src/support/utils.mjs | 22 ++ utility/generate-ttfa-ranges.mjs | 264 ++++++++++++++ verdafile.mjs | 91 +++-- 9 files changed, 932 insertions(+), 31 deletions(-) create mode 100644 font-src/gen/ttfa-controls/index.mjs create mode 100644 font-src/generated/ttfa-ranges.mjs create mode 100644 utility/generate-ttfa-ranges.mjs diff --git a/changes/26.2.3.md b/changes/26.2.3.md index 0291892db..94744f89b 100644 --- a/changes/26.2.3.md +++ b/changes/26.2.3.md @@ -5,3 +5,4 @@ - COMBINING DOUBLE VERTICAL STROKE OVERLAY (`U+20E6`). - COMBINING LONG DOUBLE SOLIDUS OVERLAY (`U+20EB`). * Improve glyphs for COLON SIGN (`U+20A1`), GUARANI SIGN (`U+20B2`), and CEDI SIGN (`U+20B5`). +* Generate TTFAutohint control files for better glyph display for variant glyphs (#1963). diff --git a/font-src/gen/build-font.mjs b/font-src/gen/build-font.mjs index 7771ea7f3..8207a42c9 100644 --- a/font-src/gen/build-font.mjs +++ b/font-src/gen/build-font.mjs @@ -7,6 +7,7 @@ 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"; +import { generateTtfaControls } from "./ttfa-controls/index.mjs"; export async function buildFont(argv, para) { const baseFont = CreateEmptyFont(argv); @@ -30,6 +31,7 @@ export async function buildFont(argv, para) { if (cache.isUpdated()) { await Caching.save(argv.oCache, argv.menu.version, cache, true); } - const font = convertOtd(baseFont, otl, finalGs); - return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated() }; + const font = await convertOtd(baseFont, otl, finalGs); + const ttfaControls = await generateTtfaControls(finalGs, font.glyphs); + return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated(), ttfaControls }; } diff --git a/font-src/gen/otd-conv/glyphs.mjs b/font-src/gen/otd-conv/glyphs.mjs index 919bdd7b0..e3890cf95 100644 --- a/font-src/gen/otd-conv/glyphs.mjs +++ b/font-src/gen/otd-conv/glyphs.mjs @@ -3,7 +3,7 @@ import { Ot } from "ot-builder"; import { Point } from "../../support/geometry/point.mjs"; import * as Gr from "../../support/gr.mjs"; -import { byCode, bySpacing, byGr, byBuildOrder } from "./glyph-name.mjs"; +import { byBuildOrder, byCode, byGr, bySpacing } from "./glyph-name.mjs"; function byRank([gna, a], [gnb, b]) { return ( @@ -28,9 +28,11 @@ class MappedGlyphStore { this.m_primaryUnicodeMapping.set(u, source); } queryBySourceGlyph(source) { + if (!source) return undefined; return this.m_mapping.get(source); } queryByName(name) { + if (!name) return undefined; return this.m_nameMapping.get(name); } decideOrder() { diff --git a/font-src/gen/ttfa-controls/index.mjs b/font-src/gen/ttfa-controls/index.mjs new file mode 100644 index 000000000..a231154c6 --- /dev/null +++ b/font-src/gen/ttfa-controls/index.mjs @@ -0,0 +1,56 @@ +import ttfaRanges from "../../generated/ttfa-ranges.mjs"; +import * as Gr from "../../support/gr.mjs"; +import { ArrayUtil } from "../../support/utils.mjs"; + +export async function generateTtfaControls(gsOrig, gs) { + let ttfaControls = []; + + for (const alignment of ttfaRanges) { + ttfaControls.push(generateTTFAAlignments(alignment, gsOrig, gs)); + } + + return ttfaControls; +} + +function generateTTFAAlignments(alignment, gsOrig, gsTtf) { + let collectedGlyphs = new Map(); + for (const [lo, hi] of alignment.ranges) { + for (let lch = lo; lch <= hi; lch++) { + const go = gsOrig.queryByUnicode(lch); + if (!go) continue; + const gd = gsTtf.queryBySourceGlyph(go); + if (!gd) continue; + collectedGlyphs.set(go, gd); + } + } + + for (;;) { + let sizeBefore = collectedGlyphs.size; + + for (const [go, gd] of collectedGlyphs) { + const cvs = Gr.AnyCv.query(go); + for (const gr of cvs) { + const gnLinked = gr.get(go); + if (!gnLinked) continue; + const goLinked = gsOrig.queryByName(gnLinked); + if (!goLinked) continue; + const gdLinked = gsTtf.queryBySourceGlyph(goLinked); + if (!gdLinked) continue; + collectedGlyphs.set(goLinked, gdLinked); + } + } + + let sizeAfter = collectedGlyphs.size; + if (sizeAfter <= sizeBefore) break; + } + + const gOrd = gsTtf.decideOrder(); + const glyphIndices = Array.from(collectedGlyphs.values()).map(gd => gOrd.reverse(gd)); + const glyphIndicesRangesStr = ArrayUtil.toRanges(glyphIndices) + .map(([lo, hi]) => (lo === hi ? `${lo}` : `${lo}-${hi}`)) + .join(", "); + + const styleAdjustLine = `${alignment.scriptTag} ${alignment.featureTag} @ ${glyphIndicesRangesStr}`; + + return styleAdjustLine; +} diff --git a/font-src/generated/ttfa-ranges.mjs b/font-src/generated/ttfa-ranges.mjs new file mode 100644 index 000000000..fd9a429d7 --- /dev/null +++ b/font-src/generated/ttfa-ranges.mjs @@ -0,0 +1,514 @@ +/* eslint-disable */ +// Machine generated. Do not modify。 +export default [ + { + "scriptTag": "latn", + "featureTag": "dflt", + "ranges": [ + [ + 0, + 177 + ], + [ + 180, + 184 + ], + [ + 186, + 442 + ], + [ + 444, + 447 + ], + [ + 452, + 452 + ], + [ + 454, + 455 + ], + [ + 457, + 458 + ], + [ + 460, + 497 + ], + [ + 499, + 659 + ], + [ + 661, + 687 + ], + [ + 7424, + 7461 + ], + [ + 7531, + 7543 + ], + [ + 7545, + 7578 + ], + [ + 7680, + 7935 + ], + [ + 8192, + 8303 + ], + [ + 8352, + 8399 + ], + [ + 8450, + 8479 + ], + [ + 8483, + 8506 + ], + [ + 8508, + 8524 + ], + [ + 8526, + 8527 + ], + [ + 8579, + 8580 + ], + [ + 11360, + 11387 + ], + [ + 11390, + 11391 + ], + [ + 11776, + 11903 + ], + [ + 42786, + 42863 + ], + [ + 42865, + 42887 + ], + [ + 42891, + 42894 + ], + [ + 42896, + 42954 + ], + [ + 42960, + 42961 + ], + [ + 42963, + 42963 + ], + [ + 42965, + 42969 + ], + [ + 42997, + 42998 + ], + [ + 43002, + 43002 + ], + [ + 43824, + 43866 + ], + [ + 43872, + 43876 + ], + [ + 43878, + 43880 + ], + [ + 64256, + 64262 + ], + [ + 122624, + 122633 + ], + [ + 122635, + 122654 + ], + [ + 122661, + 122666 + ] + ] + }, + { + "scriptTag": "latb", + "featureTag": "dflt", + "ranges": [ + [ + 178, + 179 + ], + [ + 185, + 185 + ], + [ + 7522, + 7525 + ], + [ + 8304, + 8304 + ], + [ + 8308, + 8318 + ], + [ + 8320, + 8334 + ], + [ + 8336, + 8348 + ], + [ + 11388, + 11388 + ] + ] + }, + { + "scriptTag": "latp", + "featureTag": "dflt", + "ranges": [ + [ + 688, + 696 + ], + [ + 736, + 740 + ], + [ + 7468, + 7516 + ], + [ + 7579, + 7614 + ], + [ + 8305, + 8305 + ], + [ + 8319, + 8319 + ], + [ + 11389, + 11389 + ], + [ + 42864, + 42864 + ], + [ + 42994, + 42996 + ], + [ + 43000, + 43001 + ], + [ + 43868, + 43871 + ], + [ + 43881, + 43881 + ], + [ + 67456, + 67461 + ], + [ + 67463, + 67504 + ], + [ + 67506, + 67514 + ] + ] + }, + { + "scriptTag": "grek", + "featureTag": "dflt", + "ranges": [ + [ + 880, + 883 + ], + [ + 886, + 887 + ], + [ + 891, + 893 + ], + [ + 895, + 895 + ], + [ + 902, + 902 + ], + [ + 904, + 906 + ], + [ + 908, + 908 + ], + [ + 910, + 929 + ], + [ + 931, + 993 + ], + [ + 1008, + 1013 + ], + [ + 1015, + 1023 + ], + [ + 7462, + 7466 + ], + [ + 7936, + 7957 + ], + [ + 7960, + 7965 + ], + [ + 7968, + 8005 + ], + [ + 8008, + 8013 + ], + [ + 8016, + 8023 + ], + [ + 8025, + 8025 + ], + [ + 8027, + 8027 + ], + [ + 8029, + 8029 + ], + [ + 8031, + 8061 + ], + [ + 8064, + 8071 + ], + [ + 8080, + 8087 + ], + [ + 8096, + 8103 + ], + [ + 8112, + 8116 + ], + [ + 8118, + 8123 + ], + [ + 8126, + 8126 + ], + [ + 8130, + 8132 + ], + [ + 8134, + 8139 + ], + [ + 8144, + 8147 + ], + [ + 8150, + 8155 + ], + [ + 8160, + 8172 + ], + [ + 8178, + 8180 + ], + [ + 8182, + 8187 + ], + [ + 43877, + 43877 + ] + ] + }, + { + "scriptTag": "grek", + "featureTag": "sups", + "ranges": [ + [ + 890, + 890 + ], + [ + 7517, + 7521 + ], + [ + 7615, + 7615 + ] + ] + }, + { + "scriptTag": "cyrl", + "featureTag": "dflt", + "ranges": [ + [ + 1024, + 1153 + ], + [ + 1162, + 1327 + ], + [ + 7296, + 7304 + ], + [ + 7467, + 7467 + ], + [ + 42560, + 42605 + ], + [ + 42624, + 42651 + ] + ] + }, + { + "scriptTag": "grek", + "featureTag": "subs", + "ranges": [ + [ + 7526, + 7530 + ] + ] + }, + { + "scriptTag": "cyrl", + "featureTag": "sups", + "ranges": [ + [ + 7544, + 7544 + ], + [ + 42623, + 42623 + ], + [ + 42652, + 42653 + ], + [ + 122928, + 122960 + ], + [ + 122987, + 122989 + ] + ] + }, + { + "scriptTag": "cyrl", + "featureTag": "subs", + "ranges": [ + [ + 122961, + 122986 + ] + ] + } +]; diff --git a/font-src/index.mjs b/font-src/index.mjs index 111734b72..f91c58919 100644 --- a/font-src/index.mjs +++ b/font-src/index.mjs @@ -22,10 +22,13 @@ export default main; async function main(argv) { const paraT = await getParameters(); const para = paraT(argv); - const { font, glyphStore, cacheUpdated } = await buildFont(argv, para); + const { font, glyphStore, cacheUpdated, ttfaControls } = await buildFont(argv, para); if (argv.oCharMap) { await saveCharMap(argv, glyphStore); } + if (argv.oTtfaControls) { + await fs.promises.writeFile(argv.oTtfaControls, ttfaControls.join("\n") + "\n"); + } if (argv.o) { if (para.compatibilityLigatures) await buildCompatLigatures(para, font); await saveTTF(argv.o, font); diff --git a/font-src/support/utils.mjs b/font-src/support/utils.mjs index 1de582122..33bd61281 100644 --- a/font-src/support/utils.mjs +++ b/font-src/support/utils.mjs @@ -114,5 +114,27 @@ export const ArrayUtil = { }, insertSliceAt(a, i, b) { a.splice(i, 0, ...b); + }, + // Convert character array to array of ranges. Input may be unsorted. + // The output ranges has both ends inclusive. + toRanges(chars) { + chars.sort((a, b) => a - b); + + const ranges = []; + let range = null; + + for (const ch of chars) { + if (!range) { + range = [ch, ch]; + ranges.push(range); + } else if (ch === range[1] + 1) { + range[1] = ch; + } else { + range = [ch, ch]; + ranges.push(range); + } + } + + return ranges; } }; diff --git a/utility/generate-ttfa-ranges.mjs b/utility/generate-ttfa-ranges.mjs new file mode 100644 index 000000000..d4225bb1f --- /dev/null +++ b/utility/generate-ttfa-ranges.mjs @@ -0,0 +1,264 @@ +import fs from "fs"; + +import { ArrayUtil } from "../font-src/support/utils.mjs"; + +setImmediate(() => { + main().catch(e => { + console.error(e); + process.exit(1); + }); +}); + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +async function main() { + for (const target of Targets) { + await target.filter.load(); + } + + const results = new Map(); + + nextChar: for (let lch = 0; lch < 0x20000; lch++) { + for (const target of Targets) { + if (target.filter.has(lch)) { + const resultKey = `${target.scriptTag}-${target.featureTag}`; + let result = results.get(resultKey); + if (!result) { + result = []; + results.set(resultKey, result); + } + result.push(lch); + + continue nextChar; + } + } + } + + let out = []; + + for (const [key, value] of results) { + out.push({ + scriptTag: key.split("-")[0], + featureTag: key.split("-")[1], + ranges: ArrayUtil.toRanges(value) + }); + } + + await fs.promises.writeFile( + "font-src/generated/ttfa-ranges.mjs", + `/* eslint-disable */\n` + + `// Machine generated. Do not modify。\n` + + `export default ` + + JSON.stringify(out, null, "\t") + + ";\n" + ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class InUnicodeDataSet { + constructor(subpath) { + this.subpath = subpath; + this.dataset = null; + } + + async load() { + if (this.dataset) return; + const d = (await import(`@unicode/unicode-15.0.0/${this.subpath}/code-points.js`)).default; + this.dataset = new Set(d); + } + + has(lch) { + return this.dataset.has(lch); + } +} + +class InScriptDataSet extends InUnicodeDataSet { + constructor(script) { + super(`Script/${script}`); + } +} + +class InBlockDataSet extends InUnicodeDataSet { + constructor(block) { + super(`Block/${block}`); + } +} + +class InGeneralCategoryDataSet extends InUnicodeDataSet { + constructor(general_category) { + super(`General_Category/${general_category}`); + } +} + +class InString { + constructor(s) { + this.s = s; + this.dataset = null; + } + + async load() { + if (this.dataset) return; + this.dataset = new Set(this.s); + } + + has(lch) { + return this.dataset.has(String.fromCodePoint(lch)); + } +} + +class Negation { + constructor(operand) { + this.operand = operand; + } + async load() { + await this.operand.load(); + } + has(lch) { + return !this.operand.has(lch); + } +} + +class Conjunct { + constructor(operands) { + this.operands = operands; + } + async load() { + for (const operand of this.operands) await operand.load(); + } + has(lch) { + for (const operand of this.operands) if (!operand.has(lch)) return false; + return true; + } +} + +class Disjunct { + constructor(operands) { + this.operands = operands; + } + async load() { + for (const operand of this.operands) await operand.load(); + } + has(lch) { + for (const operand of this.operands) if (operand.has(lch)) return true; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const Script = s => new InScriptDataSet(s); +const Block = b => new InBlockDataSet(b); +const GeneralCategory = gc => new InGeneralCategoryDataSet(gc); +const In = s => new InString(s); + +const All = (...operands) => new Conjunct(operands); +const Either = (...operands) => new Disjunct(operands); +const Not = operand => new Negation(operand); + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const LatinBase = { + scriptTag: "latn", + featureTag: "dflt", + filter: Either( + All( + Script("Latin"), + Either(GeneralCategory("Uppercase_Letter"), GeneralCategory("Lowercase_Letter")), + Not(Block("Halfwidth_And_Fullwidth_Forms")) + ), + Block("Currency_Symbols"), + All(Block("Letterlike_Symbols"), Not(In("℀℁⅍℠℡™℻"))) + ) +}; +const CyrillicBase = { + scriptTag: "cyrl", + featureTag: "dflt", + filter: All( + Script("Cyrillic"), + Either(GeneralCategory("Uppercase_Letter"), GeneralCategory("Lowercase_Letter")) + ) +}; +const GreekBase = { + scriptTag: "grek", + featureTag: "dflt", + filter: All( + Script("Greek"), + Either(GeneralCategory("Uppercase_Letter"), GeneralCategory("Lowercase_Letter")) + ) +}; + +const LatinSubscript = { + scriptTag: "latb", + featureTag: "dflt", + filter: Either(In("ₐₑₔₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ")) +}; +const GreekSubscript = { + scriptTag: "grek", + featureTag: "subs", + filter: Either(In("ᵦᵧᵨᵩᵪ")) +}; +const CyrillicSubscript = { + scriptTag: "cyrl", + featureTag: "subs", + filter: Either(In("𞁑𞁒𞁓𞁔𞁧𞁕𞁖𞁗𞁘𞁩𞁙𞁨𞁚𞁛𞁜𞁝𞁞𞁟𞁠𞁡𞁢𞁣𞁪𞁤𞁥𞁦")) +}; + +const LatinSuperscript = { + scriptTag: "latp", + featureTag: "dflt", + filter: Either(All(Script("Latin"), Either(GeneralCategory("Modifier_Letter")))) +}; +const GreekSuperscript = { + scriptTag: "grek", + featureTag: "sups", + filter: All(Script("Greek"), Either(GeneralCategory("Modifier_Letter"))) +}; +const CyrillicSuperscript = { + scriptTag: "cyrl", + featureTag: "sups", + filter: All(Script("Cyrillic"), Either(GeneralCategory("Modifier_Letter"))) +}; + +const DigitBase = { + scriptTag: "latn", + featureTag: "dflt", + filter: Either(In("0123456789")) +}; +const DigitSubscript = { + scriptTag: "latb", + featureTag: "dflt", + filter: Either(In("₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎")) +}; +const DigitSuperscript = { + scriptTag: "latb", + featureTag: "dflt", + filter: Either(In("⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾")) +}; + +const LatinPunctuation = { + scriptTag: "latn", + featureTag: "dflt", + filter: Either( + Block("Basic_Latin"), + Block("Latin_1_Supplement"), + Block("General_Punctuation"), + Block("Supplemental_Punctuation") + ) +}; + +const Targets = [ + LatinBase, + LatinSubscript, + LatinSuperscript, + GreekBase, + GreekSubscript, + GreekSuperscript, + CyrillicBase, + CyrillicSubscript, + CyrillicSuperscript, + DigitBase, + DigitSubscript, + DigitSuperscript, + LatinPunctuation +]; diff --git a/verdafile.mjs b/verdafile.mjs index 9db857488..201ac3087 100644 --- a/verdafile.mjs +++ b/verdafile.mjs @@ -379,22 +379,25 @@ const DistUnhintedTTF = file.make( const charMapDir = `${BUILD}/ttf/${gr}`; const charMapPath = `${charMapDir}/${fn}.charmap.mpz`; - const noGcTtfPath = `${charMapDir}/${fn}.no-gc.ttf`; + const ttfaControlsPath = `${charMapDir}/${fn}.ttfa.txt`; if (fi.spacingDerive) { // The font is a spacing variant, and is derivable form an existing // normally-spaced variant. + + const noGcTtfPath = `${charMapDir}/${fn}.no-gc.unhinted.ttf`; + const spD = fi.spacingDerive; const [deriveFrom] = await target.need( DistUnhintedTTF(spD.prefix, spD.fileName), de(charMapDir) ); - echo.action(echo.hl.command(`Create TTF`), out.full); + echo.action(echo.hl.command(`Hint TTF`), out.full); await silently.node(`font-src/derive-spacing.mjs`, { i: deriveFrom.full, - oNoGc: noGcTtfPath, o: out.full, + oNoGc: noGcTtfPath, ...fi }); } else { @@ -415,6 +418,7 @@ const DistUnhintedTTF = file.make( const { cacheUpdated } = await silently.node("font-src/index.mjs", { o: out.full, oCharMap: charMapPath, + oTtfaControls: ttfaControlsPath, cacheFreshAgeKey: ageKey, iCache: cachePath, oCache: cacheDiffPath, @@ -437,13 +441,66 @@ const DistUnhintedTTF = file.make( } ); -const BuildNoGcTtfImpl = file.make( - (gr, f) => `${BUILD}/ttf/${gr}/${f}.no-gc.ttf`, +const BuildCM = file.make( + (gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`, async (target, output, gr, f) => { await target.need(DistUnhintedTTF(gr, f)); } ); +const BuildTtfaControls = file.make( + (gr, f) => `${BUILD}/ttf/${gr}/${f}.ttfa.txt`, + async (target, output, gr, f) => { + await target.need(DistUnhintedTTF(gr, f)); + } +); + +const DistHintedTTF = file.make( + (gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`, + async (target, out, gr, fn) => { + const [fi, hint] = await target.need( + FontInfoOf(fn), + CheckTtfAutoHintExists, + de`${out.dir}` + ); + if (fi.spacingDerive) { + // The font is a spacing variant, and is derivable form an existing + // normally-spaced variant. + + const spD = fi.spacingDerive; + const charMapDir = `${BUILD}/ttf/${gr}`; + const noGcTtfPath = `${charMapDir}/${fn}.no-gc.hinted.ttf`; + + const [deriveFrom] = await target.need( + DistHintedTTF(spD.prefix, spD.fileName), + de(charMapDir) + ); + + echo.action(echo.hl.command(`Create TTF`), out.full); + await silently.node(`font-src/derive-spacing.mjs`, { + i: deriveFrom.full, + oNoGc: noGcTtfPath, + o: out.full, + ...fi + }); + } else { + const [from, ttfaControls] = await target.need( + DistUnhintedTTF(gr, fn), + BuildTtfaControls(gr, fn) + ); + echo.action(echo.hl.command(`Hint TTF`), out.full, echo.hl.operator("<-"), from.full); + await silently.run(hint, fi.hintParams, "-m", ttfaControls.full, from.full, out.full); + } + } +); + +const BuildNoGcTtfImpl = file.make( + (gr, f) => `${BUILD}/ttf/${gr}/${f}.no-gc.hinted.ttf`, + async (target, output, gr, f) => { + await target.need(DistHintedTTF(gr, f)); + } +); + const BuildNoGcTtf = task.make( (gr, fn) => `BuildNoGcTtf::${gr}/${fn}`, async (target, gr, fn) => { @@ -452,29 +509,12 @@ const BuildNoGcTtf = task.make( const [noGc] = await target.need(BuildNoGcTtfImpl(gr, fn)); return noGc; } else { - const [distUnhinted] = await target.need(DistUnhintedTTF(gr, fn)); + const [distUnhinted] = await target.need(DistHintedTTF(gr, fn)); return distUnhinted; } } ); -const BuildCM = file.make( - (gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`, - async (target, output, gr, f) => { - await target.need(DistUnhintedTTF(gr, f)); - } -); - -const DistHintedTTF = file.make( - (gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`, - async (target, out, gr, fn) => { - const [fi, hint] = await target.need(FontInfoOf(fn), CheckTtfAutoHintExists); - const [from] = await target.need(DistUnhintedTTF(gr, fn), de`${out.dir}`); - echo.action(echo.hl.command(`Hint TTF`), out.full, echo.hl.operator("<-"), from.full); - await silently.run(hint, fi.hintParams, from.full, out.full); - } -); - function formatSuffix(fmt, unhinted) { return fmt + (unhinted ? "-unhinted" : ""); } @@ -712,12 +752,9 @@ async function buildCompositeTtc(out, inputs) { async function buildGlyphSharingTtc(target, parts, out) { await target.need(de`${out.dir}`); const [ttfInputs] = await target.need(parts.map(part => BuildNoGcTtf(part.dir, part.file))); - const tmpTtc = `${out.dir}/${out.name}.unhinted.ttc`; const ttfInputPaths = ttfInputs.map(p => p.full); echo.action(echo.hl.command(`Create TTC`), out.full, echo.hl.operator("<-"), ttfInputPaths); - await silently.run(TTCIZE, "-u", ["-o", tmpTtc], ttfInputPaths); - await silently.run("ttfautohint", tmpTtc, out.full); - await rm(tmpTtc); + await silently.run(TTCIZE, "-u", ["-o", out.full], ttfInputPaths); } ///////////////////////////////////////////////////////////