From a330e899b3be43466c7c052bb7c69020d097dbfc Mon Sep 17 00:00:00 2001 From: kiliman Date: Sun, 13 Aug 2017 19:23:23 -0400 Subject: [PATCH] Initial import --- .gitattributes | 1 + .gitignore | 2 + .vscode/launch.json | 14 + README.md | 44 +++ build.bat | 15 + extract.bat | 4 + extract.js | 136 +++++++++ images/sample.html.png | Bin 0 -> 15163 bytes images/sample.js.png | Bin 0 -> 26677 bytes index.js | 285 ++++++++++++++++++ ...eratorMonoLig-MediumItalic_charstrings.xml | 130 ++++++++ .../OperatorMonoLig-MediumItalic_classdef.xml | 20 ++ .../OperatorMonoLig-MediumItalic_cmap.xml | 42 +++ .../OperatorMonoLig-MediumItalic_config.xml | 21 ++ .../OperatorMonoLig-MediumItalic_glyphs.xml | 20 ++ .../OperatorMonoLig-MediumItalic_hmtx.xml | 20 ++ .../OperatorMonoLig-MediumItalic_lookup.xml | 43 +++ .../OperatorMonoLig-MediumItalic_names.xml | 45 +++ .../OperatorMonoLig-Medium_charstrings.xml | 139 +++++++++ ligature/OperatorMonoLig-Medium_classdef.xml | 20 ++ ligature/OperatorMonoLig-Medium_cmap.xml | 42 +++ ligature/OperatorMonoLig-Medium_config.xml | 21 ++ ligature/OperatorMonoLig-Medium_glyphs.xml | 20 ++ ligature/OperatorMonoLig-Medium_hmtx.xml | 20 ++ ligature/OperatorMonoLig-Medium_lookup.xml | 42 +++ ligature/OperatorMonoLig-Medium_names.xml | 45 +++ original/.gitignore | 2 + package-lock.json | 52 ++++ package.json | 17 ++ 29 files changed, 1262 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 README.md create mode 100644 build.bat create mode 100644 extract.bat create mode 100644 extract.js create mode 100644 images/sample.html.png create mode 100644 images/sample.js.png create mode 100644 index.js create mode 100644 ligature/OperatorMonoLig-MediumItalic_charstrings.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_classdef.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_cmap.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_config.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_glyphs.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_hmtx.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_lookup.xml create mode 100644 ligature/OperatorMonoLig-MediumItalic_names.xml create mode 100644 ligature/OperatorMonoLig-Medium_charstrings.xml create mode 100644 ligature/OperatorMonoLig-Medium_classdef.xml create mode 100644 ligature/OperatorMonoLig-Medium_cmap.xml create mode 100644 ligature/OperatorMonoLig-Medium_config.xml create mode 100644 ligature/OperatorMonoLig-Medium_glyphs.xml create mode 100644 ligature/OperatorMonoLig-Medium_hmtx.xml create mode 100644 ligature/OperatorMonoLig-Medium_lookup.xml create mode 100644 ligature/OperatorMonoLig-Medium_names.xml create mode 100644 original/.gitignore create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..57798ba --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3e2e84b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +node_modules/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c0704c3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceRoot}\\index.js" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e0c790c --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Operator Mono Ligatures + +This project will generate new OpenType fonts for *Operator Mono* that includes ligatures similar to +those found in the popular Fira Code font. + +![Ligature Sample](image/sample.js.png) + +![Ligature Sample](image/sample.html.png) + + +NOTE: Because *Operator Mono* is not a free font, you must have the original font files. This utility +will merge the ligature definitions into a copy of the original font. The new font family is named *Operator Mono Lig* so you can install it side-by-side with the original font. + +## Prerequisites +* The original *Operator Mono* font... of course. +* Install *fonttools* from https://github.com/fonttools/fonttools + * Install Python + * Run: `pip install fonttools` +* Node.js + +## How to Install + +Once all the prerequisites have been installed, clone this repo. + +From the command line, run: + +``` +npm install +``` + +Copy your *Operator Mono* OpenType files into the `original` folder. + +From the command line, run: + +``` +build +``` + +This will generate the new font files int the `build` folder. You can now install these fonts on your system. + + + + + diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..ddfa804 --- /dev/null +++ b/build.bat @@ -0,0 +1,15 @@ +@echo off + +del .\original\*.ttx +ttx .\original\OperatorMono-Medium.otf +ttx .\original\OperatorMono-MediumItalic.otf + +if not exist build\* mkdir build + +node index.js OperatorMono-Medium +node index.js OperatorMono-MediumItalic + +del /q .\build\*.otf + +ttx .\build\OperatorMonoLig-Medium.ttx +ttx .\build\OperatorMonoLig-MediumItalic.ttx \ No newline at end of file diff --git a/extract.bat b/extract.bat new file mode 100644 index 0000000..28ea853 --- /dev/null +++ b/extract.bat @@ -0,0 +1,4 @@ +@echo off + +node extract.js OperatorMonoLig-Medium +node extract.js OperatorMonoLig-MediumItalic diff --git a/extract.js b/extract.js new file mode 100644 index 0000000..68ce5f2 --- /dev/null +++ b/extract.js @@ -0,0 +1,136 @@ +const Promise = require('bluebird'); +const fs = Promise.promisifyAll(require('fs')); + +const xpath = require('xpath'); +const { DOMParser, XMLSerializer } = require('xmldom'); +const format = require('xml-formatter'); +let fontName; + +const regEx = /\.liga$/; +const regExBlankLines = /^(?=\n)$|^\s*/gm; +const regExWhitespace = /^\s+$/; +const NodeType = {}; +NodeType.TEXT_NODE = 3; + +async function main() { + fontName = process.argv[2]; + + const srcFileName = `./ligature_source/${fontName}.ttx`; + + console.log(`Reading file ${srcFileName}`); + const xml = await fs.readFileAsync(srcFileName, 'utf-8'); + const dom = new DOMParser().parseFromString(xml); + + await extractAndWrite('glyphs', extractGlyphs, dom); + await extractAndWrite('cmap', extractCmap, dom); + await extractAndWrite('classdef', extractClassDef, dom); + await extractAndWrite('hmtx', extractHmtx, dom); + await extractAndWrite('lookup', extractLookup, dom); + await extractAndWrite('charstrings', extractCharStrings, dom); + + console.log('Done'); +} + +async function extractAndWrite(name, func, dom) { + const newDom = func(dom); + console.log('Finished extracting ' + name); + + const fileName = `./ligature/${fontName}_${name}.xml`; + await fs.writeFileAsync(fileName, format(serialize(newDom))); + console.log('Finished writing ' + name); +} + +const serialize = dom => + new XMLSerializer().serializeToString(dom, false, node => { + if (node.nodeType === NodeType.TEXT_NODE) { + if (regExWhitespace.test(node.data)) return null; + const data = node.data + .split('\r\n') + .filter(s => /\S+/.test(s)) + .map(s => s.replace(/^\s+/g, '')) + .join('\n'); + + return node.ownerDocument.createTextNode(data); + } + return node; + }); + +const extractGlyphs = dom => { + const newDom = new DOMParser().parseFromString(''); + const glyphIds = xpath.select('/ttFont/GlyphOrder/GlyphID', dom); + + glyphIds + .filter(node => regEx.test(node.getAttribute('name'))) + .forEach(node => newDom.documentElement.appendChild(node)); + + return newDom; +}; + +const extractCmap = dom => { + const newDom = new DOMParser().parseFromString(''); + const cmap = xpath.select('/ttFont/cmap/cmap_format_4', dom); + + cmap.forEach(node => { + // for each cmap_format_4 node, need to copy map elements + // create and append cmap_format_4 node + const cmap4 = node.cloneNode(false); + newDom.documentElement.appendChild(cmap4); + + xpath + .select('map', node) + .filter(child => regEx.test(child.getAttribute('name'))) + .forEach(child => cmap4.appendChild(child)); + }); + return newDom; +}; + +const extractClassDef = dom => { + const newDom = new DOMParser().parseFromString(''); + const classDefs = xpath.select('/ttFont/GDEF/GlyphClassDef/ClassDef', dom); + + classDefs + .filter(node => regEx.test(node.getAttribute('glyph'))) + .forEach(node => newDom.documentElement.appendChild(node)); + + return newDom; +}; + +const extractHmtx = dom => { + const newDom = new DOMParser().parseFromString(''); + const mtx = xpath.select('/ttFont/hmtx/mtx', dom); + + mtx + .filter(node => regEx.test(node.getAttribute('name'))) + .forEach(node => newDom.documentElement.appendChild(node)); + + return newDom; +}; + +const extractLookup = dom => { + const newDom = new DOMParser().parseFromString(''); + const lookup = xpath.select( + '/ttFont/GSUB/LookupList/Lookup[@index="20"]', + dom, + true + ); + + newDom.documentElement.appendChild(lookup); + + return newDom; +}; + +const extractCharStrings = dom => { + const newDom = new DOMParser().parseFromString(''); + const charStrings = xpath.select( + '/ttFont/CFF/CFFFont/CharStrings/CharString', + dom + ); + + charStrings + .filter(node => regEx.test(node.getAttribute('name'))) + .forEach(node => newDom.documentElement.appendChild(node)); + + return newDom; +}; + +main(); diff --git a/images/sample.html.png b/images/sample.html.png new file mode 100644 index 0000000000000000000000000000000000000000..921c217e94330b3695240f5ffc26c468437b9a05 GIT binary patch literal 15163 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%0DMp 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

6rCh< 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 I+%Vuj0M=tAUH||9 literal 0 HcmV?d00001 diff --git a/index.js b/index.js new file mode 100644 index 0000000..38cb98b --- /dev/null +++ b/index.js @@ -0,0 +1,285 @@ +const Promise = require('bluebird'); +const fs = Promise.promisifyAll(require('fs')); + +const xpath = require('xpath'); +const { DOMParser, XMLSerializer } = require('xmldom'); +const format = require('xml-formatter'); +let fontName; +let ligFontName; + +const regEx = /\.liga$/; +const regExBlankLines = /^(?=\n)$|^\s*/gm; +const regExWhitespace = /^\s+$/; +const NodeType = {}; +NodeType.TEXT_NODE = 3; + +async function main() { + fontName = process.argv[2] || 'OperatorMono-MediumItalic'; + ligFontName = fontName.split('-').join('Lig-'); + + const srcFileName = `./original/${fontName}.ttx`; + const dstFileName = `./build/${ligFontName}.ttx`; + console.log(`Reading original font file ${srcFileName}`); + const xml = await fs.readFileAsync(srcFileName, 'utf-8'); + const dom = new DOMParser().parseFromString(xml); + + await processPatch('names', patchNames, dom); + await processPatch('glyphs', patchGlyphs, dom); + await processPatch('cmap', patchCmap, dom); + await processPatch('classdef', patchClassDef, dom); + await processPatch('hmtx', patchHmtx, dom); + await processPatch('lookup', patchLookup, dom); + await processPatch('charstrings', patchCharStrings, dom); + + //return; + console.log(`Writing ligature font file ${dstFileName}`); + await fs.writeFileAsync(dstFileName, format(serialize(dom))); + console.log('Done'); +} + +async function loadConfigAsync(name) { + const fileName = `./ligature/${ligFontName}_${name}.xml`; + const xml = await fs.readFileAsync(fileName, 'utf-8'); + return new DOMParser().parseFromString(xml); +} + +async function processPatch(name, patchFunc, dom) { + console.log(`Patching ${name}`); + await patchFunc(dom); +} + +async function patchNames(dom) { + const configDom = await loadConfigAsync('names'); + + const names = xpath.select('/name/namerecord', configDom); + const targetName = xpath.select('/ttFont/name', dom, true); + + names.forEach(node => { + const nameId = node.getAttribute('nameID'); + const platformId = node.getAttribute('platformID'); + + // search for namerecord in target dom and replace with this one or append node + const target = xpath.select( + `/ttFont/name/namerecord[@nameID="${nameId}" and @platformID="${platformId}"]`, + dom, + true + ); + if (target) { + target.parentNode.replaceChild(node, target); + } else { + targetName.appendChild(node); + } + }); +} + +async function patchGlyphs(dom) { + const configDom = await loadConfigAsync('glyphs'); + // get number of glyphs in target dom + const targetGlyphs = xpath.select('/ttFont/GlyphOrder', dom, true); + const glyphsCount = xpath.select('count(GlyphID)', targetGlyphs, true); + + let n = glyphsCount; + // get ligature glyphs + const glyphs = xpath.select('/GlyphOrder/GlyphID', configDom); + glyphs.forEach(node => { + node.setAttribute('id', n++); + targetGlyphs.appendChild(node); + }); + + // update glyph count + setAttribute(dom, '/ttFont/maxp/numGlyphs', 'value', n); +} + +async function patchCmap(dom) { + const configDom = await loadConfigAsync('cmap'); + const cmaps = xpath.select('/cmap/cmap_format_4', configDom); + + cmaps.forEach(node => { + const platformId = node.getAttribute('platformID'); + const platEncId = node.getAttribute('platEncID'); + + // search for cmap_format_4 in target dom + const target = xpath.select( + `/ttFont/cmap/cmap_format_4[@platformID="${platformId}" and @platEncID="${platEncId}"]`, + dom, + true + ); + if (target) { + // append all map elements to target cmap_format_4 + const maps = xpath.select('map', node); + maps.forEach(child => target.appendChild(child)); + } + }); +} + +async function patchClassDef(dom) { + const configDom = await loadConfigAsync('classdef'); + const glyphClassDef = xpath.select('/ttFont/GDEF/GlyphClassDef', dom, true); + + const classDefs = xpath.select('/GlyphClassDef/ClassDefs', configDom); + classDefs.forEach(node => glyphClassDef.appendChild(node)); +} + +async function patchHmtx(dom) { + const hmtxDom = await loadConfigAsync('hmtx'); + const targetHmtx = xpath.select('/ttFont/hmtx', dom, true); + + const mtx = xpath.select('/hmtx/mtx', hmtxDom); + mtx.forEach(node => targetHmtx.appendChild(node)); + + const mtxCount = xpath.select('count(/ttFont/hmtx/mtx)', dom, true); + setAttribute(dom, '/ttFont/hhea/numberOfHMetrics', 'value', mtxCount); + + const configDom = await loadConfigAsync('config'); + copyConfigAttribute(dom, configDom, '/ttFont/head/xMin', 'value'); + copyConfigAttribute(dom, configDom, '/ttFont/head/yMin', 'value'); + copyConfigAttribute(dom, configDom, '/ttFont/head/xMax', 'value'); + copyConfigAttribute(dom, configDom, '/ttFont/head/yMax', 'value'); + copyConfigAttribute(dom, configDom, '/ttFont/hhea/advanceWidthMax', 'value'); + copyConfigAttribute(dom, configDom, '/ttFont/hhea/xMaxExtent', 'value'); + copyConfigAttribute(dom, configDom, '/ttFont/CFF/CFFFont/FontBBox', 'value'); + copyConfigAttribute( + dom, + configDom, + '/ttFont/CFF/CFFFont/Private/nominalWidthX', + 'value' + ); +} + +async function patchLookup(dom) { + const configDom = await loadConfigAsync('lookup'); + const featureList = xpath.select('/ttFont/GSUB/FeatureList', dom, true); + + // look for feature with FeatureTag liga + let featureRecord; + let featureTag; + let feature; + let featureTags = xpath.select( + '/ttFont/GSUB/FeatureList/FeatureRecord/FeatureTag[@value="liga"]', + dom + ); + + if (featureTags.length) { + featureTag = featureTags[0]; + featureRecord = featureTag.parentNode; + feature = xpath.select('Feature', featureRecord, true); + } else { + // need to add feature to FeatureList + const featuresCount = xpath.select( + 'count(/ttFont/GSUB/FeatureList/FeatureRecord)', + dom, + true + ); + + featureRecord = dom.createElement('FeatureRecord'); + featureRecord.setAttribute('index', featuresCount); + featureTag = dom.createElement('FeatureTag'); + featureTag.setAttribute('value', 'liga'); + feature = dom.createElement('Feature'); + featureRecord.appendChild(featureTag); + featureRecord.appendChild(feature); + featureList.appendChild(featureRecord); + } + let lookupListIndex = xpath.select( + 'LookupListIndex[@value="20"]', + feature, + true + ); + if (!lookupListIndex) { + const count = xpath.select('count(LookupListIndex)', feature, true); + lookupListIndex = dom.createElement('LookupListIndex'); + lookupListIndex.setAttribute('index', count); + lookupListIndex.setAttribute('value', '20'); + feature.appendChild(lookupListIndex); + } + + // helper function for adding feature to ScriptRecord + const addFeature = (scriptRecord, featureRecord, lang) => { + if (!lang) return; + const index = featureRecord.getAttribute('index'); + if (xpath.select(`FeatureIndex[@value="${index}"]`, lang, true)) return; + + const count = xpath.select('count(FeatureIndex)', lang, true); + const featureIndex = dom.createElement('FeatureIndex'); + featureIndex.setAttribute('index', count); + featureIndex.setAttribute('value', index); + lang.appendChild(featureIndex); + }; + + // nead to add feature to ScriptList + const scriptList = xpath.select('/ttFont/GSUB/ScriptList', dom, true); + const scriptRecords = xpath.select('ScriptRecord', scriptList); + scriptRecords.forEach(node => { + // check for feature in DefaultLangSys + addFeature( + node, + featureRecord, + xpath.select('Script/DefaultLangSys', node, true) + ); + // add features to any other languages + xpath + .select('Script/LangSysRecord/LangSys', node) + .forEach(lang => addFeature(node, featureRecord, lang)); + }); + + // finally add LigatureSubst to Lookup index=20 + const lookupList = xpath.select('/ttFont/GSUB/LookupList', dom, true); + const newLookup = xpath.select('/LookupList/Lookup', configDom, true); + + let lookup = xpath.select( + '/ttFont/GSUB/LookupList/Lookup[@index="20"]', + dom, + true + ); + if (!lookup) { + lookupList.appendChild(newLookup); + } else { + lookupList.replaceChild(newLookup, lookup); + } +} + +async function patchCharStrings(dom) { + const configDom = await loadConfigAsync('charstrings'); + const nameDom = await loadConfigAsync('names'); + + const cffFont = xpath.select('/ttFont/CFF/CFFFont', dom, true); + + cffFont.setAttribute('name', ligFontName); + // get names from name config + const fullName = nameDom.documentElement.getAttribute('fullName'); + const familyName = nameDom.documentElement.getAttribute('familyName'); + setAttribute(cffFont, 'FullName', 'value', fullName); + setAttribute(cffFont, 'FamilyName', 'value', familyName); + + const targetCharStrings = xpath.select('CharStrings', cffFont, true); + + const charStrings = xpath.select('/CharStrings/CharString', configDom); + charStrings.forEach(node => targetCharStrings.appendChild(node)); +} + +const setAttribute = (parent, path, name, value) => { + var node = xpath.select(path, parent, true); + node.setAttribute(name, value); +}; + +const copyConfigAttribute = (dom, configDom, path, name) => { + const value = xpath.select(path, configDom, true).getAttribute(name); + setAttribute(dom, path, name, value); +}; + +const serialize = dom => + new XMLSerializer().serializeToString(dom, false, node => { + if (node.nodeType === NodeType.TEXT_NODE) { + if (regExWhitespace.test(node.data)) return null; + const data = node.data + .split('\r\n') + .filter(s => /\S+/.test(s)) + .map(s => s.replace(/^\s+/g, '')) + .join('\n'); + + return node.ownerDocument.createTextNode(data); + } + return node; + }); + +main(); diff --git a/ligature/OperatorMonoLig-MediumItalic_charstrings.xml b/ligature/OperatorMonoLig-MediumItalic_charstrings.xml new file mode 100644 index 0000000..26b9646 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_charstrings.xml @@ -0,0 +1,130 @@ + + + 107 385.31746 184.40079 rmoveto +4.36612 -7.70291 4.56085 -7.80026 4.75555 -7.89761 -49.33333 -50 -44 -25 -38.66667 0 -34.66667 0 -17.33333 19.66667 0 39.33333 0 52 36 61.33333 72 70.66667 11.63492 -43.63095 20.77249 -48.19974 29.91005 -52.76852 rrcurveto +358.73317 -8.25699 rmoveto +2.9023 -5.00752 2.98312 -5.04793 3.06395 -5.08835 -49.33333 -50 -44 -25 -38.66667 0 -34.66667 0 -17.33333 19.66667 0 39.33333 0 52 36 61.33333 72 70.66667 12.23906 -45.8965 22.35022 -50.95206 32.46135 -56.00764 rrcurveto +143.94937 -196.1438 rmoveto +89 65 rlineto +-42 46 -35.66667 44 -29.33333 42 38 46.66667 39.33333 58 40.66667 69.33333 rrcurveto +-88 31 rlineto +-24.66667 -48.66667 -25.66666 -44 -26.66667 -39.33333 -28.66667 52 -19.33333 51.33333 -10 50.66667 31.33333 29.33333 24.33334 30 17.33333 30.66667 17.33333 30.66667 8.66667 26.33333 0 22 0 22 -6.83333 16.66667 -13.66667 11.33333 -13.66667 11.33333 -17.83333 5.66667 -22 0 rrcurveto +-32.66667 0 -30.16666 -18.16667 -27.66667 -36.33333 -27.66667 -36.33333 -13.83333 -45.5 0 -54.66667 0 -12 0.33333 -9.33333 0.66667 -6.66667 -85.33333 -68.66667 -57.29137 -67.4156 -29.24939 -66.16457 -42.08394 74.16457 -25.3753 61.74895 -8.66667 49.33333 31.33333 29.33333 24.33334 30 17.33333 30.66667 rrcurveto +17.33333 30.66667 8.66667 26.33333 0 22 0 22 -6.83333 16.66667 -13.66667 11.33333 -13.66667 11.33333 -17.83333 5.66667 -22 0 -32.66667 0 -30.16666 -18.16667 -27.66667 -36.33333 -27.66667 -36.33333 -13.83333 -45.5 0 -54.66667 0 -12 0.33333 -9.33333 0.66667 -6.66667 rrcurveto +-129.33333 -104 -64.66667 -100.66667 0 -97.33333 0 -39.33333 10.66667 -31.16667 21.33333 -23 21.33333 -23 30.33334 -11.5 39.33333 0 65.33333 0 69.33334 34.66667 73.33333 69.33333 rrcurveto +33 -47 rlineto +30.66667 -38.66667 37.33333 -19.33333 44 0 64.6701 0 69.66667 35 74.66322 70 28 -39.33333 30.66667 -38.33334 33.33333 -37.33333 rrcurveto +endchar + + + 107 442.49721 378 rmoveto +65.50279 335 -102 -3 -174 -892 101 0 rlineto +433 892 rmoveto +-64.76233 -332 -109.23767 -560 101 0 175 895 rlineto +endchar + + + 107 1051 456 rmoveto +-940 0 -19 -91 941 0 rlineto +-24 -129 rmoveto +-941 0 -19 -91 941 0 rlineto +endchar + + + 657 1601 527 rmoveto +-1450 0 -19 -91 1451 0 rlineto +-64 -240 rmoveto +-1451 0 -19 -91 1451 0 rlineto +-1408 163 rmoveto +1451 0 18 91 -1450 0 rlineto +endchar + + + 657 1188 -22 rmoveto +373 251 17 91 -281 291 -74 -72 83.33333 -83 -1195.33333 0 -19 -91 1305.69879 0 75.30121 -75 -2 -11 -65.2963 -43 -1337.7037 0 -19 -91 1218.51852 0 -124.51852 -82 rlineto +endchar + + + 107 652 -22 rmoveto +373 251 17 91 -281 291 -74 -72 83.33333 -83 -659.33333 0 -19 -91 769.69879 0 75.30121 -75 -2 -11 -65.2963 -43 -801.7037 0 -19 -91 682.51852 0 -124.51852 -82 rlineto +endchar + + + 107 1009 236 rmoveto +-428 0 88 129 364 0 18 91 -320 0 159 234 -89 36 -184 -270 -506 0 -19 -91 462 0 -88 -129 -398 0 -19 -91 355 0 rlineto +-208 -305 96 -28 227 333 471 0 rlineto +endchar + + + 657 1022 726 rmoveto +-135.886 -199 -735.114 0 -19 -91 691.97517 0 -52.57901 -77 -660.39616 0 -19 -91 617.25734 0 -49.16478 -72 -592.09254 0 -19 -91 548.95372 0 -180.95372 -265 96 -28 199.56036 293 rlineto +787.43964 0 19 91 -744.46014 0 49.03873 72 719.42142 0 18 91 -675.44191 0 52.4442 77 644.99773 0 18 91 -601.01822 0 111.01822 163 rlineto +endchar + + + 107 247 -32 rmoveto +30 -84 562 271 18 103 rlineto +-569 -161 rmoveto +553 261 17 91 -461 291 -74 -72 430 -249 -2 -11 -508 -226 rlineto +endchar + + + 107 628 -22 rmoveto +373 251 17 91 -281 291 -74 -72 195.78313 -195 -758.78313 0 -18 -93 786.48148 0 -285.48148 -188 rlineto +endchar + + + 107 497 251 rmoveto +17 93 -301 0 -18 -93 rlineto +409 93 rmoveto +-18 -93 302 0 17 93 rlineto +endchar + + + 657 1178 -22 rmoveto +373 251 17 91 -281 291 -74 -72 196 -195 -1309 0 -18 -93 1336 0 -285 -188 rlineto +endchar + + + 107 256 171 rmoveto +481 -299 66 67 -527 329 rlineto +563 467 rmoveto +-534 -295 -16 -91 440 -249 74 72 -409 208 2 10 488 261 rlineto +endchar + + + 657 1592 456 rmoveto +-1183.31798 0 107.31798 71 -45 84 -374 -251 -16 -91 280 -291 74 72 -94.62 95 1190.62 0 19 91 -1300.256 0 -63.744 64 2 10 83.13364 55 1302.86636 0 rlineto +endchar + + + 1207 2119 344 rmoveto +-1169 0 -18 -93 1170 0 rlineto +-1631 360 rmoveto +-374 -251 -16 -91 280 -291 74 72 -200 201 369 0 17 93 -382 0 277 183 rlineto +213 -282 rmoveto +72 hlineto +4 17.33333 20.16667 73.16667 36.33333 129 36.33333 129 19.83334 70.83333 3.33333 12.66667 rrcurveto +-140 hlineto +-2.66667 -14 -8.5 -71.33333 -14.33333 -128.66667 -14.33333 -128.66667 -8.83334 -72.66666 -3.33333 -16.66667 rrcurveto +77 -114 rmoveto +-135 0 -26 -131 135 0 rlineto +endchar + + + 107 472 611 rmoveto +-374 -251 -16 -91 280 -291 74 72 -200.196 201 765.196 0 17 93 -777.60829 0 276.60829 183 rlineto +endchar + + + 657 472 611 rmoveto +-374 -251 -16 -91 280 -291 74 72 -200.196 201 1315.196 0 17 93 -1327.60829 0 276.60829 183 rlineto +endchar + + + 107 981 252 rmoveto +19 91 -173 0 35 179 -97 0 -35 -179 -170 0 -1 0 -172 0 35 179 -97 0 -35 -179 -171 0 -19 -91 173 0 -35 -179 rlineto +97 0 35 179 170 0 1 0 172 0 -35 -179 97 0 35 179 rlineto +endchar + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_classdef.xml b/ligature/OperatorMonoLig-MediumItalic_classdef.xml new file mode 100644 index 0000000..1fbbfbe --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_classdef.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_cmap.xml b/ligature/OperatorMonoLig-MediumItalic_cmap.xml new file mode 100644 index 0000000..3941959 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_cmap.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_config.xml b/ligature/OperatorMonoLig-MediumItalic_config.xml new file mode 100644 index 0000000..7ac5475 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_config.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_glyphs.xml b/ligature/OperatorMonoLig-MediumItalic_glyphs.xml new file mode 100644 index 0000000..6dee8e6 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_glyphs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_hmtx.xml b/ligature/OperatorMonoLig-MediumItalic_hmtx.xml new file mode 100644 index 0000000..36b4c36 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_hmtx.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_lookup.xml b/ligature/OperatorMonoLig-MediumItalic_lookup.xml new file mode 100644 index 0000000..e862fc7 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_lookup.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-MediumItalic_names.xml b/ligature/OperatorMonoLig-MediumItalic_names.xml new file mode 100644 index 0000000..e95a238 --- /dev/null +++ b/ligature/OperatorMonoLig-MediumItalic_names.xml @@ -0,0 +1,45 @@ + + + + Operator Mono Lig + + + Medium Italic + + + H&Co: Operator Mono Lig Medium Italic: 1.200 + + + Operator Mono Lig Medium Italic + + + OperatorMonoLig-MediumItalic + + + Operator Mono Lig + + + Medium Italic + + + Operator Mono Lig Medium + + + Italic + + + H&Co: Operator Mono Lig Medium Italic: 1.200 + + + Operator Mono Lig Medium Italic + + + OperatorMonoLig-MediumItalic + + + Operator Mono Lig + + + Medium Italic + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_charstrings.xml b/ligature/OperatorMonoLig-Medium_charstrings.xml new file mode 100644 index 0000000..121fd1a --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_charstrings.xml @@ -0,0 +1,139 @@ + + + 107 270.7619 245 rmoveto +21.46031 -26 57.77777 -70 rlineto +-30.66667 -48 -33.66666 -24 -36.66667 0 -22.66667 0 -17.5 7.5 -12.33333 15 -12.33333 15 -6.16667 19.16667 0 23.33333 0 43.33333 22.66667 39.33334 45.33333 35.33333 rrcurveto +494.49207 -43 rmoveto +20.63492 -25 47.87302 -58 rlineto +-30.66667 -48 -33.66666 -24 -36.66667 0 -22.66667 0 -17.5 7.5 -12.33333 15 -12.33333 15 -6.16667 19.16667 0 23.33333 0 43.33333 22.66667 39.33334 45.33333 35.33333 rrcurveto +191 256 rmoveto +-7 102 rlineto +-102.81816 1.3353 -73.66667 -18 -44.51517 -37.3353 -40 -33.54839 -20 -40.66667 0 -47.78494 0 -35.33333 18.66667 -43 37.33333 -50.66667 -88.66667 -55.33333 -44.33333 -65 0 -74.66667 0 -12 1 -11.66667 2 -11.33333 -18 22 -23.66667 29.33333 -29.33333 36.66667 rrcurveto +36 66.66667 27.66667 69.66666 19.33333 72.66667 rrcurveto +-94 11 rlineto +-9.33333 -47.33333 -15 -51.33334 -20.66667 -55.33333 -6.66667 8 -18.16666 21.16667 -29.66667 34.33333 -29.66667 34.33333 -22.66666 28.33334 -15.66667 22.33333 -15.66667 22.33333 -7.83333 19.5 0 16.66667 0 26.66667 12.66667 19.5 25.33333 12.33333 25.33333 12.33333 46 4.5 66.66667 -3.33333 rrcurveto +-7 102 rlineto +-102.81816 1.3353 -73.66667 -18 -44.51517 -37.3353 -40 -33.54839 -20 -40.66667 0 -47.78494 0 -35.33333 18.66667 -43 37.33333 -50.66667 -88.66667 -55.33333 -44.33333 -65 0 -74.66667 0 -43.33333 13.16667 -35.16667 26.33333 -27 26.33333 -27 37.83334 -13.5 49.33333 0 rrcurveto +69.33333 0 58.33334 33.33333 47.33333 66.66667 46.66667 -64.66667 94.66666 -33.33333 142.66667 -2 69.33333 0 58.33334 33.33333 47.33333 66.66667 19.33333 -24 26 -33.66667 32.66667 -43.33333 rrcurveto +91 69 rlineto +-12.66667 16 -35.33333 43.66667 -58 71.33333 36 66.66667 27.66667 69.66666 19.33333 72.66667 rrcurveto +-94 11 rlineto +-9.33333 -47.33333 -15 -51.33334 -20.66667 -55.33333 -6.66667 8 -18.16666 21.16667 -29.66667 34.33333 -29.66667 34.33333 -22.66666 28.33334 -15.66667 22.33333 -15.66667 22.33333 -7.83333 19.5 0 16.66667 0 26.66667 12.66667 19.5 25.33333 12.33333 25.33333 12.33333 46 4.5 66.66667 -3.33333 rrcurveto +endchar + + + 107 672 709 rmoveto +-892 105 901 vlineto +-454 -9 rmoveto +-892 105 901 vlineto +410 -1418 rmoveto +0 vmoveto +endchar + + + 107 1028 458 rmoveto +-956 -2 0 -94 956 2 rlineto +-126 vmoveto +-956 -2 0 -94 956 2 rlineto +endchar + + + 657 72 526 rmoveto +0 -94 1506 2 0 94 rlineto +-331 vmoveto +-1506 -2 0 -94 1506 2 rlineto +-1506 159 rmoveto +1506 2 0 94 -1506 -2 rlineto +endchar + + + 657 1245 -20 rmoveto +333 273 0 93 -328 271 -62 -80 99 -79 -1215 -2 0 -94 1332 2 74 -59 0 -11 -70 -56 -1336 -2 0 -94 1219 2 -106 -86 rlineto +endchar + + + 107 695 -20 rmoveto +333 273 0 93 -328 271 -62 -80 99 -79 -665 -2 0 -94 782 2 74 -59 0 -11 -70 -56 -786 -2 0 -94 669 2 -106 -86 rlineto +endchar + + + 107 1028 238 rmoveto +-457 -1 65 126 392 1 0 94 -344 -1 121 238 -96 46 -145 -284 -492 -1 0 -94 445 1 -65 -126 -380 -1 0 -94 333 1 rlineto +-153 -300 96 -43 175 343 505 1 rlineto +endchar + + + 657 969 741 rmoveto +-109 -214 -788 -1 0 -94 740 1 -38 -76 -702 -1 0 -94 654 1 -34 -67 -620 -1 0 -94 572 1 -132 -259 96 -43 154 302 rlineto +816 1 0 94 -768 -1 34 67 734 1 0 94 -686 -1 39 76 647 1 0 94 -599 -1 86 168 rlineto +endchar + + + 107 308 -101 rmoveto +534 262 -2 96 -583 -292 rlineto +60 131 rmoveto +523 257 0 126 -518 254 -62 -80 480 -232 0 -11 -483 -236 rlineto +endchar + + + 107 695 -24 rmoveto +333 273 0 93 -328 271 -62 -80 235 -188 -801 0 0 -95 806 0 -243 -196 rlineto +endchar + + + 107 910 345 rmoveto +-320 -95 312 hlineto +-392 95 rmoveto +-320 -95 312 hlineto +endchar + + + 657 1245 -24 rmoveto +333 273 0 93 -328 271 -62 -80 235 -188 -1351 0 0 -95 1356 0 -243 -196 rlineto +endchar + + + 107 791 -101 rmoveto +-534 262 2 96 583 -292 rlineto +-60 131 rmoveto +-523 257 0 126 518 254 62 -80 -480 -232 0 -11 483 -236 rlineto +endchar + + + 657 405 -20 rmoveto +-333 273 0 93 328 271 62 -80 -99 -79 1215 -2 0 -94 -1332 2 -74 -59 0 -11 70 -56 1336 -2 0 -94 -1219 2 106 -86 rlineto +endchar + + + 1207 -98 852 rmoveto +1025 -507 rmoveto +-9 -95 1194 0 8 95 rlineto +-1502 hmoveto +-383.28206 0 236.28206 190 -60 79 -331 -272 0 -93 329 -273 63 80 -242.08546 194 380.08546 0 rlineto +120 -5 rmoveto +74 hlineto +0.66667 15.33333 6.66666 72.33334 12.66667 129.33333 12.66667 129.33333 6.66666 71.66667 0.66667 14 rrcurveto +-142 hlineto +0 -14 4.66667 -71.5 9.33333 -129 9.33333 -129 4.66667 -72.5 0 -16 rrcurveto +105 -114 rmoveto +-144 0 -3 -131 143 0 rlineto +endchar + + + 107 405 -24 rmoveto +-333 273 0 93 328 271 62 -80 -235 -188 801 0 0 -95 -806 0 243 -196 rlineto +endchar + + + 657 405 -24 rmoveto +-333 273 0 93 328 271 62 -80 -235 -188 1351 0 0 -95 -1356 0 243 -196 rlineto +endchar + + + 107 983 251 rmoveto +93 -168 178 -99 -178 -163 vlineto +-6 0 -162 0 0 178 -99 0 0 -178 -169 0 0 -93 169 0 0 -178 99 0 0 178 162 0 6 0 163 0 0 -178 rlineto +99 178 hlineto +endchar + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_classdef.xml b/ligature/OperatorMonoLig-Medium_classdef.xml new file mode 100644 index 0000000..e882b44 --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_classdef.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_cmap.xml b/ligature/OperatorMonoLig-Medium_cmap.xml new file mode 100644 index 0000000..3941959 --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_cmap.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_config.xml b/ligature/OperatorMonoLig-Medium_config.xml new file mode 100644 index 0000000..9bc08fe --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_config.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_glyphs.xml b/ligature/OperatorMonoLig-Medium_glyphs.xml new file mode 100644 index 0000000..6dee8e6 --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_glyphs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_hmtx.xml b/ligature/OperatorMonoLig-Medium_hmtx.xml new file mode 100644 index 0000000..3a50f38 --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_hmtx.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_lookup.xml b/ligature/OperatorMonoLig-Medium_lookup.xml new file mode 100644 index 0000000..32b33b1 --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_lookup.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ligature/OperatorMonoLig-Medium_names.xml b/ligature/OperatorMonoLig-Medium_names.xml new file mode 100644 index 0000000..f98a22b --- /dev/null +++ b/ligature/OperatorMonoLig-Medium_names.xml @@ -0,0 +1,45 @@ + + + + Operator Mono Lig + + + Medium + + + H&Co: Operator Mono Lig Medium: 1.200 + + + Operator Mono Lig Medium + + + OperatorMonoLig-Medium + + + Operator Mono Lig + + + Medium + + + Operator Mono Lig Medium + + + Regular + + + H&Co: Operator Mono Lig Medium: 1.200 + + + Operator Mono Lig Medium + + + OperatorMonoLig-Medium + + + Operator Mono Lig + + + Medium + + \ No newline at end of file diff --git a/original/.gitignore b/original/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/original/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..16b30c5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,52 @@ +{ + "name": "operator-mono-lig", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "xml-formatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-1.0.1.tgz", + "integrity": "sha1-OAgz3dC86iwJht7+u6cfhDhPNT0=", + "requires": { + "xml-parser-xo": "2.1.3" + } + }, + "xml-parser-xo": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-2.1.3.tgz", + "integrity": "sha1-TqjrhW36TddcSrVLJRVY3grcHGE=", + "requires": { + "debug": "2.6.8" + } + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" + }, + "xpath": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.24.tgz", + "integrity": "sha1-Gt4WLhzFI8jTn8fQavwW6iFvKfs=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..75fe6bc --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "operator-mono-lig", + "version": "1.0.0", + "description": "Patch Operator Mono font to include ligatures", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "kiliman@gmail.com", + "license": "ISC", + "dependencies": { + "bluebird": "^3.5.0", + "xml-formatter": "^1.0.1", + "xmldom": "^0.1.27", + "xpath": "0.0.24" + } +}