Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30a6c098e7 | |||
| 42e64f84b8 | |||
| 124d893fe5 | |||
| e692fcfbf8 | |||
| 321486d19e | |||
| 6f3d00ae6d | |||
| 420eeefe59 | |||
| a5c360bf30 | |||
| 93d4d03ef5 | |||
| 59435e4ee8 | |||
| cad9759288 | |||
| d3940070ed | |||
| 320fb069f7 | |||
| d6c1df83ed | |||
| 0b29866a3e | |||
| 9a70d50924 | |||
| 68741971f1 | |||
| bfae141c75 | |||
| 844b55f7b2 | |||
| 0c226a60bc | |||
| 2dd2079309 | |||
| 6f14cd709a | |||
| 06b5464fd6 | |||
| dc2d35f1c5 | |||
| 0823ed98af | |||
| 7b6eb72e66 | |||
| 53e9951957 | |||
| 44fc9b5ead | |||
| a86034b38d | |||
| c1bb4a9197 | |||
| 77244583f0 | |||
| f26cecc4bf | |||
| 732a29a79f | |||
| 258531d577 |
@@ -48,6 +48,24 @@
|
||||
"doc",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CandySunPlus",
|
||||
"name": "NikSun",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/502700?v=4",
|
||||
"profile": "https://github.com/CandySunPlus",
|
||||
"contributions": [
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Jakob6174",
|
||||
"name": "Jakob Pearson",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/40262817?v=4",
|
||||
"profile": "https://github.com/Jakob6174",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
root: ./docs
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: New ligature
|
||||
about: Use this template for requesting new ligatures.
|
||||
title: "Create `[LIGATURE NAME]` ligature"
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
<!-- Please only request a single ligature per issue, unless you would like something which has both symmetrical pairs (e.g. {{ and }}) -->
|
||||
|
||||
## Request preferences
|
||||
|
||||
* Weight requested: _State which weight you would like, and whether it is for SSm or not._
|
||||
|
||||
|
||||
## Ligature description
|
||||
|
||||
_Please descirbe the ligature here as best you can. Feel free to use images/screenshots to help us get ideas of how to implement it._
|
||||
|
||||
---
|
||||
|
||||
### Ligature variations
|
||||
|
||||
Due to how development is handled, not all ligatures are created at the same time. However, we will get to them all eventually. If you would like to help us develop ligatures, please reach out to us via email at kiliman@gmail.com
|
||||
|
||||
#### Operator Mono
|
||||
|
||||
- [ ] XLight
|
||||
- [ ] XLight Italic
|
||||
- [ ] Light
|
||||
- [ ] Light Italic
|
||||
- [ ] Book
|
||||
- [ ] Book Italic
|
||||
- [ ] Medium
|
||||
- [ ] Medium Italic
|
||||
- [ ] Bold
|
||||
- [ ] Bold Italic
|
||||
|
||||
#### Operator Mono SSm
|
||||
|
||||
- [ ] SSm Light
|
||||
- [ ] SSm Light Italic
|
||||
- [ ] SSm Book
|
||||
- [ ] SSm Book Italic
|
||||
- [ ] SSm Medium
|
||||
- [ ] SSm Medium Italic
|
||||
- [ ] SSm Bold
|
||||
- [ ] SSm Bold Italic
|
||||
@@ -1,4 +1,5 @@
|
||||
build
|
||||
node_modules
|
||||
.idea
|
||||
.vscode/launch.json
|
||||
.DS_Store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Operator Mono Ligatures
|
||||
|
||||
[](#contributors-)
|
||||
[](#contributors-)
|
||||
|
||||
<img src="./images/operator-mono-lig.png" />
|
||||
|
||||
@@ -94,7 +94,7 @@ If you use Docker, you can also use the executable Docker image [here](https://g
|
||||
docker-compose run --rm operator-mono-lig
|
||||
```
|
||||
|
||||
The image will build and execute the conversion. The new font files will be generated into the `output` folder.
|
||||
The image will build and execute the conversion. The new font files will be generated into the `build` folder.
|
||||
|
||||
## Configure Your Code Editor
|
||||
|
||||
@@ -156,6 +156,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://github.com/mskelton"><img src="https://avatars3.githubusercontent.com/u/25914066?v=4" width="100px;" alt="Mark Skelton"/><br /><sub><b>Mark Skelton</b></sub></a><br /><a href="#design-mskelton" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://github.com/drod3763"><img src="https://avatars2.githubusercontent.com/u/7785716?v=4" width="100px;" alt="Derick Rodriguez"/><br /><sub><b>Derick Rodriguez</b></sub></a><br /><a href="https://github.com/kiliman/operator-mono-lig/commits?author=drod3763" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/amitkparekh"><img src="https://avatars3.githubusercontent.com/u/7276308?v=4" width="100px;" alt="Amit Parekh"/><br /><sub><b>Amit Parekh</b></sub></a><br /><a href="https://github.com/kiliman/operator-mono-lig/commits?author=amitkparekh" title="Documentation">📖</a> <a href="#design-amitkparekh" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://github.com/CandySunPlus"><img src="https://avatars0.githubusercontent.com/u/502700?v=4" width="100px;" alt="NikSun"/><br /><sub><b>NikSun</b></sub></a><br /><a href="#design-CandySunPlus" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://github.com/Jakob6174"><img src="https://avatars3.githubusercontent.com/u/40262817?v=4" width="100px;" alt="Jakob Pearson"/><br /><sub><b>Jakob Pearson</b></sub></a><br /><a href="https://github.com/kiliman/operator-mono-lig/commits?author=Jakob6174" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -3,19 +3,25 @@ setlocal
|
||||
|
||||
if not exist .\build\* mkdir build
|
||||
|
||||
set flags=
|
||||
if "%1"=="--italics-hack-off" (
|
||||
set flags=%1
|
||||
shift
|
||||
)
|
||||
rem only build passed in font
|
||||
if not "%1%"=="" (
|
||||
call :build_font %1
|
||||
if not "%1"=="" (
|
||||
call :build_font %1 %flags%
|
||||
exit /b
|
||||
)
|
||||
|
||||
rem build all fonts
|
||||
for /d %%d in (.\ligature\*) do call :build_font %%~nd
|
||||
for /d %%d in (.\ligature\*) do call :build_font %%~nd %flags%
|
||||
exit /b
|
||||
|
||||
|
||||
:build_font
|
||||
set lig=%1
|
||||
set flags=%2
|
||||
set otf=%lig:Lig-=-%
|
||||
|
||||
if not exist .\original\%otf%.otf exit /b
|
||||
@@ -23,6 +29,6 @@ if not exist .\ligature\%lig%\glyphs\* exit /b
|
||||
|
||||
@echo Building %lig%
|
||||
ttx -f .\original\%otf%.otf
|
||||
node index.js %otf%
|
||||
node index.js %otf% %flags%
|
||||
ttx -f .\build\%lig%.ttx
|
||||
exit /b
|
||||
|
||||
@@ -4,6 +4,7 @@ mkdir -p build
|
||||
|
||||
build_font() {
|
||||
lig="$1"
|
||||
flags="$2"
|
||||
otf=${lig/Lig-/-}
|
||||
|
||||
if [ ! -e "./original/$otf.otf" ]
|
||||
@@ -17,17 +18,24 @@ build_font() {
|
||||
|
||||
echo Building $1
|
||||
ttx -f "./original/$otf.otf"
|
||||
node index.js $otf
|
||||
node index.js $otf $flags
|
||||
|
||||
ttx -f ./build/$1.ttx
|
||||
}
|
||||
|
||||
flags=
|
||||
if [ "$1" = "--italics-hack-off" ]
|
||||
then
|
||||
flags=$1
|
||||
shift
|
||||
fi
|
||||
if [ -n "$1" ]
|
||||
then
|
||||
# build specified font
|
||||
build_font $1
|
||||
build_font $1 $flags
|
||||
else
|
||||
# build all available fonts
|
||||
for d in ./ligature/*/ ; do
|
||||
build_font $(basename $d)
|
||||
build_font $(basename $d) $flags
|
||||
done
|
||||
fi
|
||||
|
||||
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 321 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,4 @@
|
||||
# Introduction
|
||||
|
||||
Testing
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Table of contents
|
||||
|
||||
* [Introduction](README.md)
|
||||
* [Installation instructions](installation-instructions.md)
|
||||
* [Changelog](changelog.md)
|
||||
|
||||
## Developing ligatures
|
||||
|
||||
* [Setting up your development environment](developing-ligatures/setting-up-your-development-environment.md)
|
||||
* [Designing your first ligature](developing-ligatures/designing-your-first-ligature.md)
|
||||
* [Committing your Glyphs file](developing-ligatures/committing-your-glyphs-file.md)
|
||||
* [Ensuring ligatures work at small sizes](developing-ligatures/ensuring-ligatures-work-at-small-sizes.md)
|
||||
|
||||
## Ligature Recipes
|
||||
|
||||
* [www](ligature-recipes/www.md)
|
||||
* [Double-braces](ligature-recipes/double-braces/README.md)
|
||||
* [Roman variant](ligature-recipes/double-braces/roman-variant.md)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
description: See what has changed each release.
|
||||
---
|
||||
|
||||
# Changelog
|
||||
|
||||
###
|
||||
|
||||
## 15.0.0 - 2042-12-0
|
||||
|
||||
### Fixed
|
||||
|
||||
* Removed humans, they weren't doing fine with animals.
|
||||
|
||||
### Changed
|
||||
|
||||
* Animals are now super cute, all of them.
|
||||
|
||||
## 14.0.0 - 2042-10-06
|
||||
|
||||
### Added
|
||||
|
||||
* Introduced animals into the world, we believe they're going to be a neat addition.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Committing your Glyphs file
|
||||
|
||||
The Glyphs file is very difficult to merge, so standard tools like `git rebase` don't work well. I have created some tools that will allow you to update your existing Glyphs file on top of the current.
|
||||
|
||||
{% hint style="warning" %}
|
||||
Make sure you have created a separate branch for your changes and you've committed all your changes to the branch.
|
||||
{% endhint %}
|
||||
|
||||
1. When you're ready to issue a PR for your changes, you need to run the `./tools/glyphysrebase.sh` tool.
|
||||
2. You can now create your PR, or continue making changes and running the rebase tool periodically to keep up-to-date with master.
|
||||
|
||||
## How the tool works
|
||||
|
||||
1. Checks out a clean copy of `master` in a temporary folder.
|
||||
2. Copies your current working copy to a backup folder.
|
||||
3. Copies the master copy into your working copy \(basically resetting everything back before your changes\).
|
||||
4. Runs the `./tools/copyallglyphs.sh` script to copy ligatures from your backup folder to the current master in your working copy.
|
||||
5. At this point, you now have a glyphs file that includes your custom ligatures on top of the current master.
|
||||
6. The "rebased" files are then committed.
|
||||
7. You can now issue a PR. This should easily merge into master.
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Designing your first ligature
|
||||
|
||||
## Getting Super Powers
|
||||
|
||||
Becoming a super hero is a fairly straight forward process:
|
||||
|
||||
```
|
||||
$ give me super-powers
|
||||
```
|
||||
|
||||
{% hint style="info" %}
|
||||
Super-powers are granted randomly so please submit an issue if you're not happy with yours.
|
||||
{% endhint %}
|
||||
|
||||
Once you're strong enough, save the world:
|
||||
|
||||
```
|
||||
// Ain't no code for that yet, sorry
|
||||
echo 'You got to trust me on this, I saved the world'
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Ensuring ligatures work at small sizes
|
||||
|
||||
After creating each ligature, you going to need to make sure it works at very small sizes. Ensure the brace works at very small sizes by looking at it when its very small, by dragging up from the bottom of the editor. This need to be done because as [ScreenSmart fonts are guaranteed to work at a minimum of 9px](https://www.typography.com/fonts/operator/how-to-use#setting_text_and_headlines):
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
description: Things you will need to create additional ligatures.
|
||||
---
|
||||
|
||||
# Setting up your development environment
|
||||
|
||||
## Getting Super Powers
|
||||
|
||||
Becoming a super hero is a fairly straight forward process:
|
||||
|
||||
```
|
||||
$ give me super-powers
|
||||
```
|
||||
|
||||
{% hint style="info" %}
|
||||
Super-powers are granted randomly so please submit an issue if you're not happy with yours.
|
||||
{% endhint %}
|
||||
|
||||
Once you're strong enough, save the world:
|
||||
|
||||
```
|
||||
// Ain't no code for that yet, sorry
|
||||
echo 'You got to trust me on this, I saved the world'
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
description: How to install the ligatures
|
||||
---
|
||||
|
||||
# Installation instructions
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
description: Recipe for the double-brace ligature
|
||||
---
|
||||
|
||||
# Double-braces
|
||||
|
||||
## Creating the ligature \(Roman\)
|
||||
|
||||
1.
|
||||

|
||||
|
||||
For whichever brace is the "inner-brace", change the dimensions as so, changing only the size \(the `%` in the middle column\) and the rotation \(the right most setting\):
|
||||
|
||||

|
||||
|
||||
Move the left-most brace to the left and the right-most brace to the left so the top/bottom parts cross the middle guide and the numbers are like so:
|
||||
|
||||

|
||||
|
||||
Create a new guide and move the guide so it intersects the pointy inner-corner of the larger brace
|
||||
|
||||

|
||||
|
||||
Move the smaller brace vertically \(up/down\) so the two pointy inner-corner bits cross like so:
|
||||
|
||||

|
||||
|
||||
## Creating the italic ligature
|
||||
|
||||
Repeat all the steps as above ligature above **BUT set the middle guide** **to**:
|
||||
|
||||

|
||||
|
||||
## Final ligatures
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
description: How to create the Roman variant of the double-brace ligature
|
||||
---
|
||||
|
||||
# Roman variant
|
||||
|
||||
{% hint style="info" %}
|
||||
|
||||
{% endhint %}
|
||||
|
||||
1. Add the `braceleft` component twice, or `braceright` component twice.
|
||||
2. Create a guide \(Right-click -> Add Guide\)
|
||||
3. Set the settings on the guide as so:
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# www
|
||||
|
||||
@@ -12,7 +12,33 @@ let lookupIndex = 0;
|
||||
let chainIndex = 0;
|
||||
const substLookupMap = {};
|
||||
|
||||
const buildGsubTables = (_dom, ligature) => {
|
||||
const initGsubTables = (_dom, options) => {
|
||||
dom = _dom;
|
||||
|
||||
gsubDom = xpath.select('ttFont/GSUB', dom, true);
|
||||
|
||||
// look for 'calt' feature
|
||||
featureListDom = xpath.select('FeatureList', gsubDom, true);
|
||||
lookupListDom = xpath.select('LookupList', gsubDom, true);
|
||||
|
||||
if (options.italicsHack) {
|
||||
// remove all features from FeatureList
|
||||
Array.from(featureListDom.childNodes).forEach((c) => {
|
||||
featureListDom.removeChild(c);
|
||||
});
|
||||
|
||||
Array.from(xpath.select('ttFont/GSUB//FeatureIndex', dom)).forEach((c) => {
|
||||
c.parentNode.removeChild(c);
|
||||
});
|
||||
|
||||
// remove all lookups from LookupList
|
||||
Array.from(lookupListDom.childNodes).forEach((c) =>
|
||||
lookupListDom.removeChild(c)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const buildGsubTables = (_dom, ligature, options) => {
|
||||
dom = _dom;
|
||||
|
||||
if (/_/.test(ligature.glyph) === false) {
|
||||
@@ -48,7 +74,7 @@ const buildGsubTables = (_dom, ligature) => {
|
||||
true
|
||||
);
|
||||
featureRecord = createElementWithAttributes('FeatureRecord', {
|
||||
index: featureListCount
|
||||
index: featureListCount,
|
||||
});
|
||||
featureRecord.appendChild(
|
||||
createElementWithAttributes('FeatureTag', { value: 'calt' })
|
||||
@@ -66,11 +92,10 @@ const buildGsubTables = (_dom, ligature) => {
|
||||
addFeatureToScriptList('DFLT', featureIndex);
|
||||
addFeatureToScriptList('latn', featureIndex);
|
||||
|
||||
lookupListDom = xpath.select('LookupList', gsubDom, true);
|
||||
lookupIndex = xpath.select('count(Lookup)', lookupListDom, true);
|
||||
|
||||
const lookupDom = createElementWithAttributes('Lookup', {
|
||||
index: lookupIndex++
|
||||
index: lookupIndex++,
|
||||
});
|
||||
appendChildren(
|
||||
lookupDom,
|
||||
@@ -87,7 +112,7 @@ const buildGsubTables = (_dom, ligature) => {
|
||||
featureDom.appendChild(
|
||||
createElementWithAttributes('LookupListIndex', {
|
||||
index: featureLookupCount,
|
||||
value: lookupDom.getAttribute('index')
|
||||
value: lookupDom.getAttribute('index'),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -122,12 +147,11 @@ const finalizeGsubTables = () => {
|
||||
lookupListDom,
|
||||
false
|
||||
);
|
||||
substLookups.forEach(substLookup => {
|
||||
substLookups.forEach((substLookup) => {
|
||||
substLookup.setAttribute('value', newIndex);
|
||||
});
|
||||
lookup.setAttribute('index', newIndex++);
|
||||
lookupListDom.appendChild(lookup);
|
||||
lookupListDom.appendChild(lookup);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -150,20 +174,20 @@ const addFeatureToScriptList = (tag, featureIndex) => {
|
||||
defaultLangSys.appendChild(
|
||||
createElementWithAttributes('FeatureIndex', {
|
||||
index: count,
|
||||
value: featureIndex
|
||||
value: featureIndex,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const buildBacktrackFirst = glyphs => {
|
||||
const buildBacktrackFirst = (glyphs) => {
|
||||
// backtrack = first glyph
|
||||
// input = first glphy
|
||||
// lookAhead = [1..n]
|
||||
return buildChainContext([glyphs[0]], [glyphs[0]], glyphs.slice(1));
|
||||
};
|
||||
|
||||
const buildLookAheadLast = glyphs => {
|
||||
const buildLookAheadLast = (glyphs) => {
|
||||
// backtrack = none
|
||||
// input = first glyph
|
||||
// lookAhead = [1..n] + [n]
|
||||
@@ -186,12 +210,12 @@ const buildLigatureSubst = (glyphs, lookup) => {
|
||||
);
|
||||
};
|
||||
|
||||
const getLIGs = length => new Array(length).fill('LIG', 0, length);
|
||||
const getLIGs = (length) => new Array(length).fill('LIG', 0, length);
|
||||
|
||||
const buildChainContext = (backtrack, input, lookAhead, substitute) => {
|
||||
const chainDom = createElementWithAttributes('ChainContextSubst', {
|
||||
index: chainIndex++,
|
||||
Format: 3
|
||||
Format: 3,
|
||||
});
|
||||
|
||||
buildCoverage(chainDom, 'BacktrackCoverage', backtrack);
|
||||
@@ -218,7 +242,7 @@ const buildCoverage = (chainDom, tagName, coverage) => {
|
||||
coverage.forEach((glyph, i) => {
|
||||
const coverageDom = createElementWithAttributes(tagName, {
|
||||
index: i++,
|
||||
Format: 1
|
||||
Format: 1,
|
||||
});
|
||||
coverageDom.appendChild(
|
||||
createElementWithAttributes('Glyph', { value: glyph })
|
||||
@@ -235,7 +259,7 @@ const getSubstLookup = (input, output, lookupDom) => {
|
||||
|
||||
if (!lookupDom) {
|
||||
lookupDom = createElementWithAttributes('Lookup', {
|
||||
index: Object.keys(substLookupMap).length + 1
|
||||
index: Object.keys(substLookupMap).length + 1,
|
||||
});
|
||||
lookupDom.appendChild(
|
||||
createElementWithAttributes('LookupType', { value: 1 })
|
||||
@@ -251,7 +275,7 @@ const getSubstLookup = (input, output, lookupDom) => {
|
||||
if (!singleSubstDom) {
|
||||
singleSubstDom = createElementWithAttributes('SingleSubst', {
|
||||
index: 0,
|
||||
Format: 2
|
||||
Format: 2,
|
||||
});
|
||||
lookupDom.appendChild(singleSubstDom);
|
||||
}
|
||||
@@ -272,17 +296,18 @@ const getSubstLookup = (input, output, lookupDom) => {
|
||||
|
||||
const createElementWithAttributes = (tagName, attributes) => {
|
||||
const element = dom.createElement(tagName);
|
||||
Object.entries(attributes).forEach(attribute => {
|
||||
Object.entries(attributes).forEach((attribute) => {
|
||||
element.setAttribute(attribute[0], attribute[1]);
|
||||
});
|
||||
return element;
|
||||
};
|
||||
|
||||
const appendChildren = (node, ...children) => {
|
||||
children.forEach(child => node.appendChild(child));
|
||||
children.forEach((child) => node.appendChild(child));
|
||||
};
|
||||
|
||||
//const serialize = dom => new XMLSerializer().serializeToString(dom);
|
||||
|
||||
exports.initGsubTables = initGsubTables;
|
||||
exports.buildGsubTables = buildGsubTables;
|
||||
exports.finalizeGsubTables = finalizeGsubTables;
|
||||
|
||||
@@ -17,8 +17,12 @@ const NodeType = {};
|
||||
NodeType.TEXT_NODE = 3;
|
||||
|
||||
let dom;
|
||||
let italicsHack = false;
|
||||
function main() {
|
||||
fontName = process.argv[2];
|
||||
italicsHack =
|
||||
fontName.includes('Italic') && process.argv[3] !== '--italics-hack-off';
|
||||
|
||||
ligFontName = fontName.split('-').join('Lig-');
|
||||
|
||||
const srcFileName = `./original/${fontName}.ttx`;
|
||||
@@ -29,16 +33,14 @@ function main() {
|
||||
const profiles = getProfiles();
|
||||
|
||||
// process settings (there may be more than one font to build)
|
||||
profiles.forEach(profile => buildFont(profile));
|
||||
profiles.forEach((profile) => buildFont(profile, { italicsHack }));
|
||||
|
||||
console.log('Done');
|
||||
}
|
||||
|
||||
const buildFont = profile => {
|
||||
const buildFont = (profile, options) => {
|
||||
// add suffix to dstFileName if present
|
||||
const dstFileName = `./build/${ligFontName}${
|
||||
profile.suffixWithLeadingHyphen
|
||||
}.ttx`;
|
||||
const dstFileName = `./build/${ligFontName}${profile.suffixWithLeadingHyphen}.ttx`;
|
||||
console.log(
|
||||
`Building ligature font file ${dstFileName} name = ${profile.name}`
|
||||
);
|
||||
@@ -46,19 +48,19 @@ const buildFont = profile => {
|
||||
const ligatures = sortLigatures(
|
||||
fs
|
||||
.readdirSync(`./ligature/${ligFontName}/glyphs`)
|
||||
.filter(file => /\.xml$/.test(file))
|
||||
.map(file => file.replace('.xml', ''))
|
||||
.filter((file) => /\.xml$/.test(file))
|
||||
.map((file) => file.replace('.xml', ''))
|
||||
)
|
||||
.filter(name => !/\d+\.liga$/.test(name)) // skip alternates (ends with .#)
|
||||
.filter(name => filterLigatures(name, profile.ligatures))
|
||||
.map(name => mapLigatures(name, profile.ligatures))
|
||||
.filter(entry => filterGlyphsExists(entry));
|
||||
.filter((name) => !/\d+\.liga$/.test(name)) // skip alternates (ends with .#)
|
||||
.filter((name) => filterLigatures(name, profile.ligatures))
|
||||
.map((name) => mapLigatures(name, profile.ligatures))
|
||||
.filter((entry) => filterGlyphsExists(entry));
|
||||
|
||||
const ligaturesWithLIG = ligatures;
|
||||
//[...ligatures, { name: 'LIG', glyph: 'LIG' }];
|
||||
|
||||
processPatch('names', patchNames, dom, ligatures, profile);
|
||||
processPatch('gsub', patchGsub, dom, ligatures);
|
||||
processPatch('gsub', patchGsub, dom, ligatures, options);
|
||||
processPatch('charstrings', patchCharStrings, dom, ligaturesWithLIG);
|
||||
processPatch('glyphs', patchGlyphs, dom, ligaturesWithLIG);
|
||||
processPatch('hmtx', patchHmtx, dom);
|
||||
@@ -77,8 +79,8 @@ const getProfiles = () => {
|
||||
suffix: '',
|
||||
suffixWithLeadingSpace: '',
|
||||
suffixWithLeadingHyphen: '',
|
||||
ligatures: []
|
||||
}
|
||||
ligatures: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -87,9 +89,9 @@ const getProfiles = () => {
|
||||
|
||||
const content = fs.readFileSync(profilePath, 'utf-8');
|
||||
content
|
||||
.split(os.EOL)
|
||||
.filter(line => /^[#]/.test(line) === false && line.length > 0)
|
||||
.forEach(line => {
|
||||
.split(/\r|\r\n|\n/g)
|
||||
.filter((line) => /^[#]/.test(line) === false && line.length > 0)
|
||||
.forEach((line) => {
|
||||
const ch = line.trim()[0];
|
||||
if (ch === '[') {
|
||||
let name = line.substr(1, line.indexOf(']') - 1);
|
||||
@@ -98,12 +100,12 @@ const getProfiles = () => {
|
||||
suffix: name === 'default' ? '' : name,
|
||||
suffixWithLeadingSpace: name === 'default' ? '' : ' ' + name,
|
||||
suffixWithLeadingHyphen: name === 'default' ? '' : '-' + name,
|
||||
ligatures: []
|
||||
ligatures: [],
|
||||
};
|
||||
profiles.push(profile);
|
||||
} else {
|
||||
if (!profile) {
|
||||
throw new Error('You must profile a profile name in []');
|
||||
throw new Error('You must provide a profile name in []');
|
||||
}
|
||||
profile.ligatures.push(line);
|
||||
}
|
||||
@@ -115,12 +117,12 @@ const getProfiles = () => {
|
||||
const filterLigatures = (name, mappings) => {
|
||||
// loop through mappings and return if name applies or not
|
||||
// skip if setting is !name
|
||||
return mappings.filter(entry => entry === '!' + name).length === 0;
|
||||
return mappings.filter((entry) => entry === '!' + name).length === 0;
|
||||
};
|
||||
|
||||
const mapLigatures = (name, mappings) => {
|
||||
let mapping = { name, glyph: name };
|
||||
mappings.forEach(entry => {
|
||||
mappings.forEach((entry) => {
|
||||
const [val1, val2] = entry.split('=');
|
||||
// handle lig=altliga
|
||||
if (val1 === name) {
|
||||
@@ -137,10 +139,10 @@ const filterGlyphsExists = ({ glyph }) => {
|
||||
return exists;
|
||||
};
|
||||
|
||||
const sortLigatures = ligatures => {
|
||||
const sortLigatures = (ligatures) => {
|
||||
// sort by most glyphs then alphabetically
|
||||
const sorted = ligatures
|
||||
.map(ligature => {
|
||||
.map((ligature) => {
|
||||
return { count: ligature.split('_').length + 1, ligature: ligature };
|
||||
})
|
||||
.sort(
|
||||
@@ -148,7 +150,7 @@ const sortLigatures = ligatures => {
|
||||
-compareProperty(a.count, b.count) || // sort by count descending
|
||||
compareProperty(a.ligature, b.ligature) // then by ligature alphabetically
|
||||
)
|
||||
.map(entry => entry.ligature);
|
||||
.map((entry) => entry.ligature);
|
||||
return sorted;
|
||||
};
|
||||
|
||||
@@ -160,20 +162,20 @@ const compareProperty = (a, b) => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadXml = name => {
|
||||
const loadXml = (name) => {
|
||||
const fileName = `./ligature/${ligFontName}/${name}.xml`;
|
||||
const xml = fs.readFileSync(fileName, 'utf-8');
|
||||
return new DOMParser().parseFromString(xml);
|
||||
};
|
||||
|
||||
const processPatch = (name, patchFunc, dom, ligatures, profile) => {
|
||||
const processPatch = (name, patchFunc, dom, ligatures, profile, options) => {
|
||||
console.log(`Patching ${name}`);
|
||||
patchFunc(dom, ligatures, profile);
|
||||
patchFunc(dom, ligatures, profile, options);
|
||||
};
|
||||
|
||||
const PlatformId = {
|
||||
mac: 1,
|
||||
win: 3
|
||||
win: 3,
|
||||
};
|
||||
|
||||
const NameId = {
|
||||
@@ -184,7 +186,7 @@ const NameId = {
|
||||
version: 5,
|
||||
postscriptName: 6,
|
||||
windowsFamilyName: 16,
|
||||
fontStyleName: 17
|
||||
fontStyleName: 17,
|
||||
};
|
||||
|
||||
const patchNames = (dom, ligatures, profile) => {
|
||||
@@ -216,7 +218,8 @@ const patchNames = (dom, ligatures, profile) => {
|
||||
setAttribute(cffFont, 'FamilyName', 'value', familyNamePlat);
|
||||
|
||||
// update existing names with new names
|
||||
updateName(PlatformId.mac, NameId.familyName, familyName);
|
||||
// mac family name does not include style
|
||||
updateName(PlatformId.mac, NameId.familyName, familyNamePlat);
|
||||
updateName(PlatformId.mac, NameId.fontStyle, names.fontStyle);
|
||||
updateName(PlatformId.mac, NameId.uniqueId, uniqueId);
|
||||
updateName(PlatformId.mac, NameId.fullName, fullName);
|
||||
@@ -224,6 +227,7 @@ const patchNames = (dom, ligatures, profile) => {
|
||||
updateName(PlatformId.mac, NameId.windowsFamilyName, familyNamePlat);
|
||||
updateName(PlatformId.mac, NameId.fontStyleName, names.fontStyle);
|
||||
|
||||
// windows family name includes style
|
||||
updateName(PlatformId.win, NameId.familyName, familyName);
|
||||
updateName(PlatformId.win, NameId.fontStyle, names.windowsFontStyle);
|
||||
updateName(PlatformId.win, NameId.uniqueId, uniqueId);
|
||||
@@ -253,7 +257,7 @@ const patchGlyphs = (dom, ligatures) => {
|
||||
// only import glyphs specified
|
||||
let n = glyphsCount;
|
||||
// get ligature glyphs
|
||||
ligatures.forEach(ligature => {
|
||||
ligatures.forEach((ligature) => {
|
||||
targetGlyphs.appendChild(
|
||||
createElementWithAttributes('GlyphID', { id: n++, name: ligature.glyph })
|
||||
);
|
||||
@@ -262,18 +266,19 @@ const patchGlyphs = (dom, ligatures) => {
|
||||
setAttribute(dom, '/ttFont/maxp/numGlyphs', 'value', n);
|
||||
};
|
||||
|
||||
const patchGsub = (dom, ligatures) => {
|
||||
const patchGsub = (dom, ligatures, options) => {
|
||||
// don't patch if no ligatures other than LIG
|
||||
if (ligatures.length <= 1) return;
|
||||
gsub.initGsubTables(dom, options);
|
||||
|
||||
ligatures.forEach(ligature => {
|
||||
ligatures.forEach((ligature) => {
|
||||
// build gsub tables
|
||||
gsub.buildGsubTables(dom, ligature);
|
||||
gsub.buildGsubTables(dom, ligature, options);
|
||||
});
|
||||
gsub.finalizeGsubTables();
|
||||
};
|
||||
|
||||
const patchHmtx = dom => {
|
||||
const patchHmtx = (dom) => {
|
||||
const mtxCount = xpath.select('count(/ttFont/hmtx/mtx)', dom, true);
|
||||
setAttribute(dom, '/ttFont/hhea/numberOfHMetrics', 'value', mtxCount);
|
||||
|
||||
@@ -306,7 +311,7 @@ const patchCharStrings = (dom, ligatures) => {
|
||||
);
|
||||
const targetGsubrs = xpath.select('/ttFont/CFF/GlobalSubrs', dom, true);
|
||||
const fingerprints = {};
|
||||
ligatures.forEach(ligature => {
|
||||
ligatures.forEach((ligature) => {
|
||||
console.log(
|
||||
`* ${ligature.name}${
|
||||
ligature.name === ligature.glyph ? '' : ' => ' + ligature.glyph
|
||||
@@ -318,11 +323,11 @@ const patchCharStrings = (dom, ligatures) => {
|
||||
|
||||
const subrs = {
|
||||
sourcePath: '/Glyph/Subrs',
|
||||
target: targetSubrs
|
||||
target: targetSubrs,
|
||||
};
|
||||
const gsubrs = {
|
||||
sourcePath: '/Glyph/GlobalSubrs',
|
||||
target: targetGsubrs
|
||||
target: targetGsubrs,
|
||||
};
|
||||
|
||||
patchCharStringSubrs(glyphDom, node, fingerprints, subrs, gsubrs);
|
||||
@@ -332,16 +337,16 @@ const patchCharStrings = (dom, ligatures) => {
|
||||
createElementWithAttributes('mtx', {
|
||||
name: ligature.glyph,
|
||||
width: glyphDom.getAttribute('width'),
|
||||
lsb: glyphDom.getAttribute('lsb')
|
||||
lsb: glyphDom.getAttribute('lsb'),
|
||||
})
|
||||
);
|
||||
const code = glyphDom.getAttribute('code');
|
||||
if (code) {
|
||||
Array.from(targetCmaps).forEach(node => {
|
||||
Array.from(targetCmaps).forEach((node) => {
|
||||
node.appendChild(
|
||||
createElementWithAttributes('map', {
|
||||
code,
|
||||
name: ligature.glyph
|
||||
name: ligature.glyph,
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -354,7 +359,7 @@ const patchCharStringSubrs = (glyphDom, node, fingerprints, subrs, gsubrs) => {
|
||||
|
||||
const lines = node.childNodes[0].textContent.split(/\r|\r\n|\n/g);
|
||||
const newLines = [];
|
||||
lines.forEach(line => {
|
||||
lines.forEach((line) => {
|
||||
if (line.trim().length === 0) return;
|
||||
const matches = line.match(/(.*?)\{([0-9a-z]+)\} (callsubr|callgsubr)$/);
|
||||
if (matches != null) {
|
||||
@@ -403,21 +408,21 @@ const copyConfigAttribute = (dom, configDom, path, name) => {
|
||||
|
||||
const createElementWithAttributes = (tagName, attributes) => {
|
||||
const element = dom.createElement(tagName);
|
||||
Object.entries(attributes).forEach(attribute => {
|
||||
Object.entries(attributes).forEach((attribute) => {
|
||||
element.setAttribute(attribute[0], attribute[1]);
|
||||
});
|
||||
return element;
|
||||
};
|
||||
|
||||
//const dump = dom => console.log(serialize(dom));
|
||||
const serialize = dom =>
|
||||
new XMLSerializer().serializeToString(dom, false, node => {
|
||||
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|\r\n|\n/g)
|
||||
.filter(s => /\S+/.test(s))
|
||||
.map(s => s.replace(/^\s+/g, ''))
|
||||
.filter((s) => /\S+/.test(s))
|
||||
.map((s) => s.replace(/^\s+/g, ''))
|
||||
.join(os.EOL);
|
||||
|
||||
return node.ownerDocument.createTextNode(data);
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
<Glyph name="colon_equal.liga" lsb="-401" width="625">
|
||||
<Glyph name="colon_equal.liga" lsb="-407" width="625">
|
||||
<CharString>
|
||||
0 129 39 77 169 77 hstem
|
||||
-553 134 512 438 vstem
|
||||
-401 388 rmoveto
|
||||
82 129 -43 77 169 77 -44 130 hstemhm
|
||||
-553 134 23 133 356 438 hintmask 01011110
|
||||
-397 447 rmoveto
|
||||
133 hlineto
|
||||
2 130 rlineto
|
||||
-133 hlineto
|
||||
779 -104 rmoveto
|
||||
hintmask 01101110
|
||||
775 -163 rmoveto
|
||||
1 77 rlineto
|
||||
-538 -77 hlineto
|
||||
-244 -278 rmoveto
|
||||
537 -246 rmoveto
|
||||
1 77 rlineto
|
||||
-538 -77 hlineto
|
||||
hintmask 10101110
|
||||
-250 -86 rmoveto
|
||||
133 hlineto
|
||||
2 130 rlineto
|
||||
-133 hlineto
|
||||
779 -98 rmoveto
|
||||
1 77 rlineto
|
||||
-538 -77 hlineto
|
||||
6 129 rlineto
|
||||
-134 hlineto
|
||||
endchar
|
||||
</CharString>
|
||||
<Subrs/>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<Glyph name="plus_equal.liga" lsb="-470" width="625">
|
||||
<CharString>
|
||||
168 77 42 77 50 77 hstem
|
||||
-273 81 205 438 vstem
|
||||
-192 287 rmoveto
|
||||
196 hlineto
|
||||
1 77 rlineto
|
||||
-197 207 -81 -207 -197 -77 197 -208 81 hlineto
|
||||
642 335 rmoveto
|
||||
1 77 rlineto
|
||||
-438 -77 hlineto
|
||||
437 -246 rmoveto
|
||||
1 77 rlineto
|
||||
-438 -77 hlineto
|
||||
endchar
|
||||
</CharString>
|
||||
<Subrs/>
|
||||
<GlobalSubrs/>
|
||||
</Glyph>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "operator-mono-lig",
|
||||
"version": "2.2.7",
|
||||
"version": "2.3.0",
|
||||
"description": "Patch Operator Mono font to include ligatures",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -12,7 +12,8 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"hash.js": "^1.1.3",
|
||||
"npm": "^5.7.1",
|
||||
"npm": "^6.13.4",
|
||||
"serialize-javascript": "^2.1.2",
|
||||
"xml-formatter": "^1.0.1",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "0.0.24"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- html comment -->
|
||||
<!-- html comment a => b && c++ -->
|
||||
|
||||
<div class="header">
|
||||
<a href="http://localhost">Click Here</a>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// @ts-nocheck
|
||||
|
||||
const func = (a, b, c, d, e, f, s, n) => {
|
||||
// this is a comment
|
||||
if (a++ && b-- || c >= d || e <= f) {
|
||||
@@ -10,6 +12,16 @@ const func = (a, b, c, d, e, f, s, n) => {
|
||||
a |> b
|
||||
a <| b |> c -> d ==> e
|
||||
|
||||
// if (a++ && b-- || c >= d || e <= f) {
|
||||
// }
|
||||
// if (s === 'string' && n !== 999) {
|
||||
// }
|
||||
// if (a == b && c != d)
|
||||
// a ?? b
|
||||
// a <- b <-- c <== d
|
||||
// a |> b
|
||||
// a <| b |> c -> d ==> e
|
||||
|
||||
// italic ligatures
|
||||
// != == >= <= -> => ==>
|
||||
// === !== && || ++ --
|
||||
|
||||