Coder Social home page Coder Social logo

arrowtype / recursive Goto Github PK

View Code? Open in Web Editor NEW
3.1K 29.0 51.0 1.67 GB

Recursive Mono & Sans is a variable font family for code & UI

Home Page: https://recursive.design

License: SIL Open Font License 1.1

Python 98.10% Shell 1.20% JavaScript 0.04% CSS 0.15% HTML 0.50%
font variable-font type-design fonttools fontmake google-fonts

recursive's Introduction

Want (infrequent) updates on new fonts from Arrow Type?

💌 Sign up for the Arrow Type newsletter! 💌

📝 Follow @ArrowType on Instagram! 📝


Recursive Sans & Mono

Learn more on the Recursive web specimen →

Recursive Sans & Mono Repo Artwork

Recursive Sans & Mono is a variable type family built for better code & UI. It is inspired by casual script signpainting, but designed primarily to meet the needs of programming environments and application interfaces.

In programming, “recursion” is when a function calls itself, using its own output as an input to yield powerful results. Recursive Mono was used as a tool to help build itself: it was used to write Python scripts to automate type production work and to generate specimen images, and it was used in HTML, CSS, and JS to create web-based proofs & prototypes. Through this active usage, Recursive Mono was crafted to be both fun to look at as well as deeply useful for all-day work.

Recursive Sans borrows glyphs from its parent mono but adjusts the widths of many key glyphs for comfortable readability. Its metrics are superplexed – every style takes up the exact same horizontal space, across all styles. In this 3-axis variable font, this allows for fluid transitions between weight, slant, and “expression” (casual to strict letterforms), all without text shifts or layout reflow. Not only does this allow for new interactive possibilities in UI, but it also makes for a uniquely fun typesetting experience.

Language Support

Recursive is designed with a modified Google Fonts Latin Expert character set, including numerous useful symbols for currencies & math (see the Character Set notes for more details), plus support for the following languages:

Abenaki, Afaan Oromo, Afar, Afrikaans, Albanian, Alsatian, Amis, Anuta, Aragonese, Aranese, Aromanian, Arrernte, Arvanitic (Latin), Asturian, Atayal, Aymara, Azerbaijani, Bashkir (Latin), Basque, Belarusian (Latin), Bemba, Bikol, Bislama, Bosnian, Breton, Cape Verdean Creole, Catalan, Cebuano, Chamorro, Chavacano, Chichewa, Chickasaw, Cimbrian, Cofán, Cornish, Corsican, Creek, Crimean Tatar (Latin), Croatian, Czech, Danish, Dawan, Delaware, Dholuo, Drehu, Dutch, English, Esperanto, Estonian, Faroese, Fijian, Filipino, Finnish, Folkspraak, French, Frisian, Friulian, Gagauz (Latin), Galician, Ganda, Genoese, German, Gikuyu, Gooniyandi, Greenlandic (Kalaallisut), Guadeloupean Creole, Gwich’in, Haitian Creole, Hän, Hawaiian, Hiligaynon, Hopi, Hotcąk (Latin), Hungarian, Icelandic, Ido, Igbo, Ilocano, Indonesian, Interglossa, Interlingua, Irish, Istro-Romanian, Italian, Jamaican, Javanese (Latin), Jèrriais, Kaingang, Kala Lagaw Ya, Kapampangan (Latin), Kaqchikel, Karakalpak (Latin), Karelian (Latin), Kashubian, Kikongo, Kinyarwanda, Kiribati, Kirundi, Klingon, Kurdish (Latin), Ladin, Latin, Latino sine Flexione, Latvian, Lithuanian, Lojban, Lombard, Low Saxon, Luxembourgish, Maasai, Makhuwa, Malay, Maltese, Manx, Māori, Marquesan, Megleno-Romanian, Meriam Mir, Mirandese, Mohawk, Moldovan, Montagnais, Montenegrin, Murrinh-Patha, Nagamese Creole, Nahuatl, Ndebele, Neapolitan, Ngiyambaa, Niuean, Noongar, Norwegian, Novial, Occidental, Occitan, Old Icelandic, Old Norse, Onĕipŏt, Oshiwambo, Ossetian (Latin), Palauan, Papiamento, Piedmontese, Polish, Portuguese, Potawatomi, Q’eqchi’, Quechua, Rarotongan, Romanian, Romansh, Rotokas, Sami (Inari Sami), Sami (Lule Sami), Sami (Northern Sami), Sami (Southern Sami), Samoan, Sango, Saramaccan, Sardinian, Scottish Gaelic, Serbian (Latin), Seri, Seychellois Creole, Shawnee, Shona, Sicilian, Silesian, Slovak, Slovenian, Slovio (Latin), Somali, Sorbian (Lower Sorbian), Sorbian (Upper Sorbian), Sotho (Northern), Sotho (Southern), Spanish, Sranan, Sundanese (Latin), Swahili, Swazi, Swedish, Tagalog, Tahitian, Tetum, Tok Pisin, Tokelauan, Tongan, Tshiluba, Tsonga, Tswana, Tumbuka, Turkish, Turkmen (Latin), Tuvaluan, Tzotzil, Uzbek (Latin), Venetian, Vepsian, Vietnamese, Volapük, Võro, Wallisian, Walloon, Waray-Waray, Warlpiri, Wayuu, Welsh, Wik-Mungkan, Wiradjuri, Wolof, Xavante, Xhosa, Yapese, Yindjibarndi, Zapotec, Zarma, Zazaki, Zulu, Zuni

License

When you are considering using a font, the font license is one of the first things you should look for and read. It outlines how you agree to use the fonts, and font licensing is different between different type projects & type foundries.

The Recursive project is licensed under the SIL Open Font License v1.1. This is a free software license that permits you to use the font software under a set of conditions. Please refer to the full text of the license for details about the permissions, conditions, and disclaimers.


Using the fonts

  1. Download the latest fonts from the Releases (Look under the "Assets" of the latest release, download the zip, and then open that zip)
  2. Install the fonts on your system

Installing fonts on your system

Mac

The easy way: open font files in Font Book, and click "Install Font".

The nice way:

  • In Finder, navigate to the folder <yourusername>/Library/Fonts
  • Drag this to the Finder sidebar, or while the folder is highlighted, use File > Add to Sidebar to do so
  • Now, you can manage new font files just like normal files! Drag in fonts. Enclose them in folders to more easily organize families. To deactivate fonts, you can either remove them from this folder or (if you might want them later) right click and "Compress" them into zip files.

Windows

Double-click the TTF file, then select "Install."

To install many static font files (these may work better in apps such as Microsoft Word and PowerPoint):

  • Go into the static_fonts folder, then into the mono or sans static_otf folder.
  • Open Font Settings, then drag-and-drop these font files into the Font Settings window.

On the web

This is a big topic, but a couple of good guides are from MDN and from CSS-Tricks.

In general, you should link in the font with @font-face, being sure to use font-weight: 300 1000; to specify the font's weight range:

@font-face {
 font-family: 'Recursive';
 src: url('path/to/font/<UPDATE_SPECIFIC_FONT_PATH>.woff2') format('woff2-variations');
 font-weight: 300 1000;
}

Then, you can use the font with both font-weight and font-variation-settings!

Notes:

  • MONO and CASL are "unregistered" axes (not currently in Microsoft's official listing of variation axes and specs), so these tags must be used in all-caps and controlled via font-variation-settings.
  • You will eventually be able to also use the property font-style to control slnt and CRSV axes, but these have some browser support issues (as of Oct 2019, but tools are rapidly evolving to better support variable fonts, so this will improve over time!) For now, these work best in font-variation-settings.
  • One weird trick: you can use CSS custom properties to more easily control font-variation-settings – read more about this technique in Pixel Ambacht's fantastic tutorial on it.

Font usage in Code Editors

There are two primary ways to use Recursive in code editors:

1: Rec Mono for Code

Install the Rec Mono fonts (found in the “Recursive_Code” folder of release downloads). These have code ligatures & several stylistic sets pre-applied so they will work by default in most code environments & apps. These are also named & configured in a way that enables their use in code themes that utilize italic & bold styles. If you wish to configure specific features in Rec Mono fonts (such as a dotted 0 or single-story g), see Recursive Code Config.

2: Standard Recursive Mono desktop fonts

These fonts are built in a more traditional way than the "Code" fonts above, so they may render slightly better in some contexts. There are pros and cons, though: Code ligatures work better in the "Code" fonts, in many monospace-only apps. On Mac before macOS 11, these do not work in themes with Italic styles – see above for “Code” fonts that do. And, the "Code" fonts allow you a bit more customization, if you want it!

To use the standard decktop fonts, install the desktop Recursive Mono fonts (found in the “Recursive_Desktop” folder of release downloads). Then, activate them & set OpenType features if your code editor allows this. Instructions for three editors follow.

In summary, open the editor settings and set the font family. These family names are shortened because long font names can cause errors in some environments. The main abbreviations are as follows:

  • Metrics:
    • Mn means Mono (monospace/fixed-width)
    • Sn means Sans (proportional/natural-width)
  • Styling:
    • Csl means Casual
    • Lnr means Linear
  • St means Static (as opposed to variable, like the full Recursive variable font)

So, you will pretty much be setting your editor to use either Recursive Mn Csl St or Recursive Mn Lnr St.

Settings for specific code editors, to use Recursive Mono desktop fonts:

⚙️ VS Code (Click to expand)

In Settings, search Font Family, then specify the family you wish to use.

For Recursive Mono Casual Regular:

"Recursive Mn Csl St", Menlo, "Apple Color Emoji",  monospace

For Recursive Mono Linear Regular:

"Recursive Mn Lnr St", Menlo, "Apple Color Emoji",  monospace

Then, if you want, you can activate OpenType features by searching in the settings for fontLigatures, then editing this in settings.json like this:

    "editor.fontLigatures": "'ss01','ss05','dlig'"

(The above would give you a simplied 6 & 9, a single-story a, and activate code ligatures.)

⚙️ Atom (Click to expand)

In the menu bar, go to Atom -> Stylesheet and add in the following:

atom-text-editor {
  font-family: 'Recursive Mn Csl St';            /* This sets the font family*/
  font-feature-settings: "ss01", "ss05", "dlig"; /* This sets OpenType features, if you want to set them. */
}

Use font-family: 'Recursive Mn Lnr St'; to get the Linear family.

⚙️ Sublime Text (Click to expand)

Go to Sublime Text -> Preferences -> Settings and set font_face to the specific PostScript name of the style you wish to use.

For Recursive Mono Casual Regular:

"font_face": "Recursive Mn Csl St",

For Recursive Mono Linear Regular:

"font_face": "Recursive Mn Lnr St",

To control code ligatures or other OpenType features, set the font_options open, like so:

"font_options": ["ss01", "ss05", "dlig"],

So, the full settings might look something like this:

{
	"font_face": "Recursive Mn Lnr St",
	"font_size": 24,
	"theme": "Adaptive.sublime-theme"
}

Variable Axes

Recursive has the following axes:

Axis Tag Range Default Description
Monospace MONO 0 to 1 0 Sans (natural-width) to Mono (fixed-width)
Casual CASL 0 to 1 0 Linear to Casual
Weight wght 300 to 1000 300 Light to ExtraBlack. Can be defined with usual font-weight property.
Slant slnt 0 to -15 0 Upright (0°) to Slanted (about 15°)
Cursive CRSV 0, 0.5, or 1 0.5 Always roman (0), auto (0.5), or always cursive (1)

Axis Definitions

Axis Definitions, Recursive

  • Monospace MONO - 0 to 1. Adjusts the glyph widths from proportional or “Sans” (0) to fixed-width or “Mono” (1).

    Recommended use: In general, the proportional design is more readable in text and UI, while the monospace design is more appropriate for code and text in which letter disambiguation is especially critical (e.g. passwords, ID numbers, tabular data, etc).

  • Casual CASL - 0 to 1. Adjusts the expressive style or “genre” of the glyphs. In Recursive, this goes from from Linear (0) to Casual (1).

    Recommended use: The Linear style shares a similar structure to fonts classified as lineal, merging aspects of humanist sans with rationalized, compact, flat-sided letterforms. This regular, familiar structure makes it appropriate for long-form text requiring focus (e.g. paragraphs, full code documents, and punchy headlines). The Casual style is inspired by single-stroke casual signpainting, but drawn for small sizes. It is most useful in shorter-form text where a warm and inviting tone is desired (e.g. blog post headlines, store signage, and computer terminals).

  • Weight wght - 300 to 1000. The overall thickness of letters and the darkness of text composed with them. Notably, in Recursive, the weight axis does not affect glyph width. A bold weight takes the same amount of space as a light weight, even at in proportional styles of the MONO axis.

    Recommended use: Differences in weight can provide emphasis in text, show user interaction, or adjust the tone of communication. For light text on dark backgrounds, 400 (“Regular”) tends to be appropriate for text and code. For dark text on a light background, it can be beneficial to adjust the weight upwards to 500 (“Medium”).

    Why isn’t it a Grade axis? See Issue #365

  • Slant slnt – 0 to -15. The "forward lean" of letters. Note: -15 (negative 15) corresponds to a 15° clockwise slant, due to type design's roots in geometry. If the Italic axis is at its default value, going past a slant of -13.99 will activate "cursive" letters, converting them to more-handwritten forms such as the simplified, "single story" a and g.

    Recommended use: Use Slant as a secondary way to emphasize text or vary typographic tone. In text, it can be useful to use a partial slant of around -9, while at display sizes, you can expect the most precise outlines at either 0 or -15. You can also animate from 0 to -13 without letterforms or glyph widths changing, which is great for things like hovered links or buttons.

  • Cursive CRSV – 0, 0.5, or 1. Controls the substitution of cursive forms along the Slant axis. "Off" (0) maintains Roman letterforms such as a "double story" a and g, "Auto" (0.5) allows for Cursive substitution, and "On" (1) asserts cursive forms even in upright text with a Slant of 0.

    Recommended use: Use Cursive as a tertiary way to emphasize text, or as a way to have more control over animated text (e.g. a hyperlink that slants upon user interaction can by styled with Cursive 0 or 1 to prevent the abrupt changes of glyph substitution).

Advanced design recommendations

In general, Recursive is intended for small-to-medium sized usage, particularly on screen. However, it is useful to understand which stylistic ranges work best in what contexts. A few guidelines worth knowing:

Style range Recommended size Recommended use case
Casual 0 (Linear), Weight 300–800 (Light–ExtraBold) 8px to 72px General use (especially for longer text)
Casual 1 (Casual), Weight 300–800 (Light–ExtraBold) 14px to 72px General use (more personality)
Weights 801–900 (Black–ExtraBlack) 32px to 144px Headlines, display typography
Intermediate values of Casual and Slant 10px to 40px Good in text, but may not look as good in display sizes

Things to be aware of:

  • If you use weights 300–800 for large text, it may look good to slightly reduce letter-spacing (also called tracking).
  • The heaviest weights of Recursive are really heavy, so they need to be a little larger to remain legible.
  • Casual and Slant axes look great with intermediate values at text sizes, but they are mostly intended to be used at either fully "on or off" values, with intermediates available to allow animated stylistic transitions. If you are setting type at large sizes, avoid intermediate CASL and slnt values. If you stick to named instances in design apps (e.g. Mono Casual Bold Italic, etc), this is handled for you automatically.
  • The Casual Italic instances are drawn to work well in text but are also the most expressive styles of the family – try them at large sizes to show off their wavy stems and really make a statement! 🏄‍♂️🏄‍♀️

OpenType Features

Recursive is built with a number of OpenType features that make it simple to control a few handy typographic features.

OpenType Features in Recursive


Building the fonts

Set up the environment

To build, set up the virtual environment

virtualenv -p python3 venv

Then activate it:

source venv/bin/activate

Then install requirements:

pip install -U -r requirements.txt

Also:

pip install git+https://github.com/LettError/DesignspaceProblems

Build the fonts

The fonts are built with the mastering/build.py script. This script can build everything (python build.py --all), or a subset of the fonts. To view all the options, type python build.py -h to see all options. The recommended build process is detailed below.

Note: There are sub-scripts for just prepping the source files (mastering/prep_fonts.py), building the source files (mastering/build_files.py), generating the variable font (mastering/build_variable.py), and generating the static fonts (build_static.py). These scripts can be handy if you just want to do one thing to the build files. Each takes a set of command line arguments, all documented (type python <script_name> -h) to view the documentation.

First, prep fonts

Before beginning, change your working directory to mastering.

cd mastering

python build.py --files is the first step. This will generate all the files needed for building the variable and static fonts. You will likely want to give the font a version number with this command (python build.py --version 1.085 --files). To prep only files for the variable font, use python build.py --varfiles, or to prep only files for the static fonts, use python build.py --statfiles.

After the files have been generated (do note that the static instances take a bit of time to generate), you will want to look at the mastering/build/static/CFF/checkoutlines.txt file. This is the report (edited to remove issues that do not need attention) from checkoutlinesUFO. Issues found in this report should be cleaned up in the static UFOs. Many issues are due to overlap removal. Nothing is perfect, overlap removal algorithms included.

To build the variable font

To build the variable font, run:

# activate venv, install dependencies, cd mastering
version=1.085 # (replace version number)
python build.py --varfiles --version $version
python build.py --variable --version $version

To build the static fonts

To build all the static fonts, run:

# activate venv, install dependencies, cd mastering 
version=1.085 # (replace version number)
python build.py --statfiles --version $version
python build.py --static --version $version

To build all the fonts

If you want to build all of the sources, fonts, and WOFF2 versions of all of the fonts run:

# activate venv, install dependencies, cd mastering
python build.py --all --version 1.085 # (replace version number)

Get notifications (Mac only)

Add option --pync (-p for short) to the script call to get Mac notifications, which may be helpful if you are working on other tasks while a build runs.

# activate venv, install dependencies, cd mastering
python build.py --all --pync --version 1.085 # (replace version number)

Making a GitHub release

First, build fonts with the mastering flow above. Then:

# navigate to the root directory of the project, then...

# update to latest font build directory
fontDir="fonts_1.085"
src/build-scripts/make-release/00-prep-release.sh $fontDir

Then, copy the latest variable font into your local Recursive Code Config repo to build updated Code fonts. Copy these into the newly-made directory fonts/ArrowType-Recursive-1.XXX/Recursive_Code.

Finally, go to the repo’s Releases page to make a new one.

Using the resources in this project for type design

This project has included a large amount of research (contained in docs/), and contains many small tools (contained in src/00-recursive-scripts-for-robofont that may help you if you are designing variable fonts with UFOs in RoboFont.

Using the project scripts in RoboFont

  1. Navigate to your robofont scripts folder in a terminal.

    1. In RoboFont's menu, go to Scripts > Reveal Scripts Folder
    2. Open a terminal window.
    3. Type cd , then copy-paste or drag-n-drop the scripts folder to get its full filepath. Hit return/enter.
  2. Make a symbolic link or "symlink" to the Recursive project scripts folder, src/00-recursive-scripts-for-robofont

    1. Still in the same terminal, type ln -s
    2. Copy-paste or drag-n-drop the src/00-recursive-scripts-for-robofont from Finder to get its full path. Hit return/enter.
    3. Check that it's there by running ls to list files. You should see src/00-recursive-scripts-for-robofont as one of the items listed.

So, this will look something like:

ln -s ~/FOLDER_PATH/robofont-scripts ~/FOLDER_PATH/recursive/src/00-recursive-scripts-for-robofont
  1. Update your Scripts menu in RoboFont with Scripts > Update Menu
    • If the Recursive scripts don't appear, you may need to restart RoboFont

Now, you can run the Recursive project scripts directly from the Scripts menu, or by opening them in the Scripting Window.

Contributing

See CONTRIBUTING.md for policies around contributing to the project.

Contributors

  • Design by Stephen Nixon, with
    • Lisa Huang
    • Katja Schimmel
    • Rafał Buchner
  • Font Mastering with Ben Kiel
  • Many other advisors and reviewers

recursive's People

Contributors

arrowtype avatar augustskare avatar benkiel avatar chrissimpkins avatar dependabot[bot] avatar katjaschimmel avatar lisahuang2017 avatar rafalbuchner avatar thundernixon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

recursive's Issues

FontMake & FontToools – AssertionError: 'Base master not found.'

AssertionError: ('Base master not found.', 'MarkBasePos', '[0]', 'Lookup', '[0]', 
'list', '.Lookup', 'LookupList', '.LookupList', 'GPOS', '.table', 'table_G_P_O_S_')

Usually, this kind of error means that none of the sources has locations that match the defaults of all axes. However, I don't think that is true in this designspace...

I've installed fontTools in editable mode to add some print statements in the relevant functions.

  1. git clone the fontTools repo to make a local copy
  2. while in the recursive project, run pip install -U fonttools -e /Users/stephennixon/type-repos/fonttools

When I add a print statement just before the error, I get this:

INFO:fontTools.varLib:Merging OpenType Layout tables
[{'slnt': -1.0, 'XPRN': 1.0}, {'XPRN': 1.0}, {'slnt': -1.0}]

I would expect for that locations list to have min and max locations for wght (and maybe ital) as well, so the problem may be there.

Full terminal output (Click to expand)
▶  src/build-scripts/build.sh src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace
+ source venv/bin/activate
++ deactivate nondestructive
++ unset -f pydoc
++ '[' -z '' ']'
++ '[' -z '' ']'
++ '[' -n /bin/sh ']'
++ hash -r
++ '[' -z '' ']'
++ unset VIRTUAL_ENV
++ '[' '!' nondestructive = nondestructive ']'
++ VIRTUAL_ENV=/Users/stephennixon/type-repos/recursive/venv
++ export VIRTUAL_ENV
++ _OLD_VIRTUAL_PATH='/Users/stephennixon/type-repos/recursive/venv/bin:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi:/usr/local/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi'
++ PATH='/Users/stephennixon/type-repos/recursive/venv/bin:/Users/stephennixon/type-repos/recursive/venv/bin:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi:/usr/local/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi'
++ export PATH
++ '[' -z '' ']'
++ '[' -z '' ']'
++ _OLD_VIRTUAL_PS1=
++ '[' x '!=' x ']'
+++ basename /Users/stephennixon/type-repos/recursive/venv
++ PS1='(venv) '
++ export PS1
++ alias pydoc
++ true
++ '[' -n /bin/sh ']'
++ hash -r
+ DS=src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace
+ [[ -z src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace ]]
+ [[ src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace = \-\-\h\e\l\p ]]
+ outputDir=font-betas/work-in-progress
++ basename src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace
+ dsName=Recursive-mono-full--w_ital_slnt.designspace
+ fontName=Recursive-mono-full--w_ital_slnt
++ timestamp
++ date +%Y_%m_%d
+ date=2019_08_10
+ fontmake -m src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace -o variable --output-path font-betas/work-in-progress/Recursive-mono-full--w_ital_slnt-2019_08_10.ttf
INFO:fontmake.font_project:Building variable font font-betas/work-in-progress/Recursive-mono-full--w_ital_slnt-2019_08_10.ttf
INFO:ufo2ft:Pre-processing glyphs
INFO:cu2qu.ufo:New spline lengths: 1: 18, 2: 1708, 3: 1139, 4: 596, 5: 135, 6: 79, 7: 20
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualASlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualA
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualBSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualB
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualCSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualC
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualB-support.w.middle
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearASlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearA
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearBSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearB
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearCSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearC
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearB-support.w.middle
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualASlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualA
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualBSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualB
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualCSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualC
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualB-support.w.middle
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearASlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearA
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearBSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearB
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearCSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearC
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearB-support.w.middle
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building variable TTF font
INFO:fontTools.varLib:Axes:
[{'default': 800.0,
  'hidden': False,
  'labelNames': {'en': 'Weight'},
  'map': [(300.0, 250.0),
          (400.0, 350.0),
          (500.0, 500.0),
          (600.0, 600.0),
          (700.0, 700.0),
          (800.0, 800.0),
          (900.0, 900.0)],
  'maximum': 900.0,
  'minimum': 300.0,
  'name': 'Weight',
  'tag': 'wght'},
 {'default': 0.0,
  'hidden': False,
  'labelNames': {'en': 'Slant'},
  'map': [],
  'maximum': 0.0,
  'minimum': -15.0,
  'name': 'Slant',
  'tag': 'slnt'},
 {'default': 0.5,
  'hidden': False,
  'labelNames': {'en': 'Italic'},
  'map': [],
  'maximum': 1.0,
  'minimum': 0.0,
  'name': 'Italic',
  'tag': 'ital'},
 {'default': 0.0,
  'hidden': False,
  'labelNames': {'en': 'Expression'},
  'map': [],
  'maximum': 1.0,
  'minimum': 0.0,
  'name': 'Expression',
  'tag': 'XPRN'}]
INFO:fontTools.varLib:Internal master locations:
[{'Expression': 1.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 250.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 250.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 800.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 800.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 900.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 900.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 500.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 250.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 250.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 800.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 800.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 900.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 900.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 500.0}]
INFO:fontTools.varLib:Internal axis supports:
{'Expression': [0.0, 0.0, 1.0],
 'Italic': [0.0, 0.5, 1.0],
 'Slant': [-15.0, 0.0, 0.0],
 'Weight': [250.0, 800.0, 900.0]}
INFO:fontTools.varLib:Normalized master locations:
[{'Expression': 1.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': -1.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': -1.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 0.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 0.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 1.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 1.0},
 {'Expression': 1.0,
  'Italic': 0.0,
  'Slant': 0.0,
  'Weight': -0.5454545454545454},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': -1.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': -1.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 0.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 0.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 1.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 1.0},
 {'Expression': 0.0,
  'Italic': 0.0,
  'Slant': 0.0,
  'Weight': -0.5454545454545454}]
INFO:fontTools.varLib:Index of base master: 10
INFO:fontTools.varLib:Building variable font
INFO:fontTools.varLib:Loading master fonts
INFO:fontTools.varLib:Generating fvar
INFO:fontTools.varLib:Generating avar
INFO:fontTools.varLib:Building variations tables
INFO:fontTools.varLib:Generating MVAR
INFO:fontTools.varLib:  xhgt: OS/2.sxHeight     [526, 526, 540, 540, 550, 550, 526, 526, 540, 540, 550, 550]
INFO:fontTools.varLib:  stro: OS/2.yStrikeoutPosition   [316, 316, 324, 324, 330, 330, 316, 316, 324, 324, 330, 330]
INFO:fontTools.varLib:  sbxo: OS/2.ySubscriptXOffset    [-19, 0, -19, 0, -19, 0, -19, 0, -19, 0, -19, 0]
INFO:fontTools.varLib:  spxo: OS/2.ySuperscriptXOffset  [88, 0, 88, 0, 88, 0, 88, 0, 88, 0, 88, 0]
INFO:fontTools.varLib:  hcrs: hhea.caretSlopeRise       [1000, 1, 1000, 1, 1000, 1, 1000, 1, 1000, 1, 1000, 1]
INFO:fontTools.varLib:  hcrn: hhea.caretSlopeRun        [250, 0, 250, 0, 250, 0, 250, 0, 250, 0, 250, 0]
INFO:fontTools.varLib:  undo: post.underlinePosition    [-75, -200, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75]
INFO:fontTools.varLib:Generating HVAR
INFO:fontTools.varLib:Merging OpenType Layout tables
Traceback (most recent call last):
  File "/Users/stephennixon/type-repos/recursive/venv/bin/fontmake", line 10, in <module>
    sys.exit(main())
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/__main__.py", line 407, in main
    project.run_from_designspace(designspace_path, **args)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 914, in run_from_designspace
    **kwargs
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 969, in _run_from_designspace_interpolatable
    designspace, output_path=output_path, output_dir=output_dir, **kwargs
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 411, in build_variable_font
    inplace=True,
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/ufo2ft/__init__.py", line 548, in compileVariableTTF
    ttfDesignSpace, exclude=excludeVariationTables, optimize=optimizeGvar
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/__init__.py", line 954, in build
    _merge_OTL(vf, model, master_fonts, axisTags)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/__init__.py", line 687, in _merge_OTL
    merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 111, in mergeTables
    for m in master_ttfs])
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 99, in mergeThings
    self.mergeLists(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 85, in mergeLists
    self.mergeThings(value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 95, in mergeThings
    mergerFunc(self, out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 798, in merge
    merger.mergeLists(self.SubTable, subtables)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 85, in mergeLists
    self.mergeThings(value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 95, in mergeThings
    mergerFunc(self, out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 626, in merge
    _MarkBasePosFormat1_merge(self, lst, merger)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 614, in _MarkBasePosFormat1_merge
    merger.mergeThings(anchor, allAnchors)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1000, in mergeThings
    model, lst = masterModel.getSubModel(lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/models.py", line 219, in getSubModel
    subModel = VariationModel(subList(key, self.origLocations), self.axisOrder)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/models.py", line 203, in __init__
    keyFunc = self.getMasterLocationsSortKeyFunc(locations, axisOrder=self.axisOrder)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/models.py", line 225, in getMasterLocationsSortKeyFunc
    assert {} in locations, "Base master not found."
AssertionError: ('Base master not found.', 'MarkBasePos', '[0]', 'Lookup', '[0]', 'list', '.Lookup', 'LookupList', '.LookupList', 'GPOS', '.table', 'table_G_P_O_S_')
                                                                                                                                                             

Should italic alternates use slant axis + stylistic sets, or have their own "italic" axis?

Most lowercase glyphs in Recursive have .italic alternates, which swap in for roman forms in Italic named instances.

However, a user also needs the ability to assert roman forms in slanted instances, and italic forms in upright instances. Partly, this is necessary to give users access to the full range of design possibilities in the family, and partly, this is necessary to allow for smooth animations – for instance, a link that slants when hovered should not swap italic forms during the transition.

There are two technical ways to achieve this:

1. Tying the glyph substitution rules to the Slant axis, then giving stylistic sets that can assert italic or roman forms.

Pros:

  • One less axis, which may make it simpler for users to understand the font, and easier for users to get what they generally want in the slant axis, probably 90% of the time.

Cons:

  • Many design tools (including Adobe software) don't make it easy to find and control stylistic sets, so most users will completely miss this option. At minimum, it will require the user to explore two separate controls to access the italic aspect of the font's designspace.
  • This may lead to more cases of broken animations, and users not realizing they can have upright italics and sloped romans.
  • From a production standpoint, it requires duplicating all default glyphs (including diacritics) to .roman glyphs, which adds a ton of extra glyphs to the sources, for no clear gain.

2. Adding an Italic axis which only controls roman/italic letterforms

Pros:

  • Allows full control of designspace on more UIs (if a user can control variation axes, they can control this, even stylistic sets aren't surfaced in a particular software context).
  • Makes it obvious that Slant and Italic axes can be used separately.
  • Named instances still allow users to easily select "Italic" style (slanted and italicized) if that's what they want.
  • Reduces the complexity of production.

Cons:

  • Potentially, the typeface will seem harder to understand for newcomers, because interfaces will surface more complexity up-front.
  • The "Italic" axis would be different from other axes, in that by its nature, it won't really allow for smooth transitions between levels (short of a "Higher-Order Interpolation" approach, which is way out of scope for the current project).

recursive axes 2019-06-29 at 16 22 07

Is there a filesize difference? Needs testing.

  • To test this, I need to add the .roman alternates to fonts in an example designspace, then generate that version.

Draw Vietnamese accents, build Vietnamese composite glyphs

We need to draw first versions of the horn and the hook, and compose for /o, /u, /O, and /U (probably in Mono casual & linear weight B).

From that, we will draw these accents in other masters.

As these are drawn, their anchors should be added to the base glyphs, as well as to the Glyph Construction recipes so they can be built along with other diacritics: https://github.com/thundernixon/recursive/tree/master/src/glyph-recipes


Super basic early sketches of what I have in mind for this:

IMG_0882


Resources:

Make first round of code ligatures

We need a starter set of code ligatures to support the most noticeable and common syntax pieces in JavaScript, Python, and Markdown.

Here is a visual of these shown (the blue glyphs are currently placeholders set in Fira Code):

image

Notes:

  • We will design these in mono masters, and they will be duplicated into the sans masters, where we will not touch them (similar to many other 600-unit glyphs)
  • These will have widths going in units of 600, based on the number of glyphs in the ligature. If it's a ligature of 2 glyphs, that will mean 1200 units, etc.
  • Along with some helpful input from Rafał, I have designed a foundational ampersand_ampersand.code in the mono weight B. This should be moved into other masters.

Steps:

  • check/test whether common text editors (VS Code, Sublime, Atom) use contextual ligatures by default. If they do, consider making some ligatures default, and others opt-in (see docs/coding-ligatures for more details)

More details and up-to-date list of priority 0 glyphs are here:

https://github.com/thundernixon/recursive/tree/master/docs/coding-ligatures

Make inktrap angles more consistent in the joints of vertical stems to horizontal serifs, across weights

So far, I've been making almost all inktraps in the upright casual styles at 0° or 90° – except for where vertical stems to horizontal serifs, and a few other isolated instances (/Z).

However, this contradicted instructions to collaborators. For the moment, I'm shelving this to come back to. I will probably make the inktraps angled in the Casual Heavy masters, because I think that looks slightly nicer.

image

image

image

easing curve could be smoother in flipbook animation

I now have an animation that is a multi-step sequence, set to an easing curve:

recursive-flipbook-axes-200_page-2019_08_08-19

(permalink to this current DrawBot script)

The one issue: the curve isn't actually working in as smooth a way as it could. At t=0.25 and t=0.75, there are "kinks" where the speed accelerates suddenly.

Below is a debug animation with the easing curve visualized, plus cyan marks placed at timing quadrants. When the magenta dot passes these marks, acceleration occurs.

This is (probably) due to how I set up my quadrant math. I modulated the y value so that it would work as an interpolation value compared to the start and endpoint along the y axis between the upper and lower t values – effectively making that section a full easing curve on its own. However, this means that quadrant 3 ends with a slow easing, then quadrant 4 begins with a fast ease. Here's a relevant part of the code:

    if frame > frames*0.25 and frame <= frames*0.5:
        xprn = (0.5, 1)
        wght = (800.01, 900 - 0.01)
        slnt = (-7.5, -15)
        currentItal = 0

        startPos = curveDict[0.25][1]
        endPos = curveDict[0.5][1]

        stepRange = endPos - startPos

        factor = (y - startPos ) / stepRange

kinks in the current easing curve

Debug animation (Click to expand)

recursive-flipbook-axes-2019_08_08-20_25

Kern sans masters

Do kerning for Sans Casual C, duplicate this to other sans masters.

Find whether alternate glyphs (.italic, etc) need separately-composed diacritics. If so, add to glyph recipes.

Is it possible to make alternate glyphs work without also adding composed glyphs for each alt?

E.g. I have g and g.italic. Do I need both gcircumflex and gcircumflex.italic, or could a designspace rule / opentype feature substitute the .italic alternates for the defaults in composed glyphs?

  • find out
  • if pre-composed glyphs are needed, add these to the glyph construction recipe.
  • make a script to write designspace rules based on glyph suffixes?

Build variable font with prop axis – AssertionError .... 'FeatureList', '.FeatureList', 'GPOS', '.table', 'table_G_P_O_S_'

In trying to build a test variable font with a proportion axis, I am running into an error which is probably connected to the Sans master having some kerning, and the Mono master having none.

AssertionError: ((2, [3, 2]), 'int', '.FeatureCount', 'FeatureList', '.FeatureList', 'GPOS', '.table', 'table_G_P_O_S_')
Full Traceback (Click to expand)
INFO:fontTools.varLib:Merging OpenType Layout tables
Traceback (most recent call last):
  File "/Users/stephennixon/type-repos/recursive/venv/bin/fontmake", line 10, in <module>
    sys.exit(main())
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/__main__.py", line 407, in main
    project.run_from_designspace(designspace_path, **args)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 914, in run_from_designspace
    **kwargs
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 969, in _run_from_designspace_interpolatable
    designspace, output_path=output_path, output_dir=output_dir, **kwargs
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 411, in build_variable_font
    inplace=True,
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/ufo2ft/__init__.py", line 548, in compileVariableTTF
    ttfDesignSpace, exclude=excludeVariationTables, optimize=optimizeGvar
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/__init__.py", line 954, in build
    _merge_OTL(vf, model, master_fonts, axisTags)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/__init__.py", line 687, in _merge_OTL
    merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 111, in mergeTables
    for m in master_ttfs])
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 101, in mergeThings
    assert allEqualTo(out, lst), (out, lst)
AssertionError: ((2, [3, 2]), 'int', '.FeatureCount', 'FeatureList', '.FeatureList', 'GPOS', '.table', 'table_G_P_O_S_')

I suspect that kerning groups and pairs may need to almost completely match between masters.

Tests:

  • make features.fea match in both masters
    • result: same build error
  • copy kerning.plist code from sans to mono UFO, but zero-out all the values
    • result: same build error
  • copy groups.plist to both masters
  • remove kerning.plist and groups.plist from both masters
  • check anchor similarity between masters
  • make sure that w has anchors present on the support layer support.w.middle
  • sort both UFOs in the same way
    • THIS WAS WHAT SOLVED IT FOR ME. Oof, that was tough to find.

make mono-sans /zero and /zero.sans compatible

All masters, mono and sans, need:

  • zero (with a slash)
  • zero.sans (with no slash)

Both must be compatible between all masters.

To simplify this, the /zero should use /zero.sans as a component, and only add in the slash.

Make sans lowercase and lowercase italic compatible with mono

We must complete #85 first.

Stephen must complete #88 next.

Task: make mono & sans lowercase glyphs compatible for building into a single variable font.

Mono to Sans: Proportion designspace strategy

Just as the italic axis requires base glyphs (like a) to be in one form, and then use a designspace rule to swap to an alternate (like a.italic), the Proportion axis will require monospace (or sans) forms of glyphs to be in the default glyph spots, and swap to alternates with rules (like moving from r to r.sans or to r.mono).

For the most part, these forms aren’t too unnatural in a mono font – mono fonts like Source Code and Atlas Typewriter already use these forms. Some are even already partially drawn in Recursive (though, still in need of upgrades of overlap removal and inktraps). An exception is l, which is really a problem when it’s missing the top-left serif. This probably needs to swap to the .italic form in the base glyphs, and then have alternates applied in the sans (and slnt axis?)

Sketch of mono-to-sans glyph swapping (Click to expand)

C3E56198-E85D-4E94-9BED-30A12BEC38BE

First, make glyphs with mono–sans alternates compatible:

Steps completed so far (Click to expand)
  • open all mono masters, then copy i l r f g to .mono names
  • use script to rename flatg to g.flatear
  • use script to bring l.italic g.flatear into default names
  • make l.sans i.sans r.sans f.sans in mono masters, at 600 width (starting from l i.italic f r), the edit them into sans forms
    • l.sans
    • i.sans
    • r.sans
    • f.sans
  • make i.sans r.sans f.sans default forms
  • make sure f g i l r f.italic i.italic l.italic r.italic f.mono g.mono i.mono l.mono r.mono l.sans are copied to Sans masters (most are, and you can tell because they will have few overlaps, plus inktrap), then adjust width to match default sans versions

    • test in casual weight B first
    • copy in others
      • simplify overlap in Mono versions of each glyph
      • match widths to Sans Casual B
        • /l must be changed to 350
      • adjust letter width without changing ratio of vertical or slanted nodes.
  • test sans-width versions of f.italic r.italic j.italic to see whether outstroke serif works, or if these need alternates/changed designs

You may have to edit or make new designspaces for this. Current ones are in src/masters/sans/partial-designspaces, with the latest one being recursive-sans-proportion-b.designspace at f288b5a.

Later:

  • work on compatibility for the rest of the lowercase letters

Stephen, later:

  • add these new adjusted-width glyphs to list of glyphs to not copy in the future
  • copy everything to sans again
  • write rules in combined designspace file to make these work together with prop + ital rules

Continue work at later point with #96

slnt needs to go 0 to -15

The slnt axis is currently ranged from 0.0 to 15.0 but sadly https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_slnt says,

Note that the scale for the Slant axis is interpreted as the angle of slant in counter-clockwise degrees from upright. This means that a typical, right-leaning oblique design will have a negative slant value. This matches the scale used for the italicAngle field in the post table.

That means the range should be 0.0 to -15.0

Correct spacing of copied punctuation in Sans Slanted masters

The following punctuation glyphs need to be newly copied into the Sans masters from updated Mono masters, then adjusted and respaced.

The widths should stay consistent for other masters, but the sidebearings will change. I have copied the previous punctuation to a new layer, proportional, to make it simple to adjust newly-copied versions to the correct width and (relatively) correct spacing.

Punctuation and metrics in Recursive Sans Casual B:

glyph width LSB RSB
asterisk 500 44 44
braceleft 500 56 29
braceright 500 29 56
bracketleft 400 40 31
bracketright 400 31 40
grave 350 42 42
parenleft 400 50 -20
parenright 400 -20 50
period 300 46 46
periodcentered 300 46 46

FontMake AssertionError: '.Coverage', 'MarkGlyphSetsDef', '.MarkGlyphSetsDef', 'GDEF', '.table', 'table_G_D_E_F_'

In my FontMake build for mono masters, I got the following error:

AssertionError: (('ringacute', ('gravecomb.case', 'ringacute', 'gravecomb.case', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute')), 'str', '[12]', 'list', '.glyphs', 'Coverage', '[2]', 'list', '.Coverage', 'MarkGlyphSetsDef', '.MarkGlyphSetsDef', 'GDEF', '.table', 'table_G_D_E_F_')

This was solved by sorting all fonts in the same way, using https://github.com/arrowtype/recursive/blob/614af40c582e8ff78216bf4f8e996cca5f84a70a/src/00-recursive-scripts-for-robofont/sort-selected-fonts.py

Full terminal output (Click to expand)
~/type-repos/recursive  master ✔                                                                                                                                                                     12m  
▶ src/build-scripts/build.sh src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace
+ source venv/bin/activate
++ deactivate nondestructive
++ unset -f pydoc
++ '[' -z '' ']'
++ '[' -z '' ']'
++ '[' -n /bin/sh ']'
++ hash -r
++ '[' -z '' ']'
++ unset VIRTUAL_ENV
++ '[' '!' nondestructive = nondestructive ']'
++ VIRTUAL_ENV=/Users/stephennixon/type-repos/recursive/venv
++ export VIRTUAL_ENV
++ _OLD_VIRTUAL_PATH='/Users/stephennixon/type-repos/recursive/venv/bin:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi:/usr/local/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi'
++ PATH='/Users/stephennixon/type-repos/recursive/venv/bin:/Users/stephennixon/type-repos/recursive/venv/bin:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/opt/ruby/bin:/usr/local/opt/python/libexec/bin:/Users/stephennixon/.npm-global/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi:/usr/local/bin:/Users/stephennixon/bin:/Users/stephennixon/Library/Python/3.7/bin:/opt/yarn-[version]/bin:/Users/stephennixon/.npm-global/binlibffi'
++ export PATH
++ '[' -z '' ']'
++ '[' -z '' ']'
++ _OLD_VIRTUAL_PS1=
++ '[' x '!=' x ']'
+++ basename /Users/stephennixon/type-repos/recursive/venv
++ PS1='(venv) '
++ export PS1
++ alias pydoc
++ true
++ '[' -n /bin/sh ']'
++ hash -r
+ DS=src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace
+ [[ -z src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace ]]
+ [[ src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace = \-\-\h\e\l\p ]]
+ outputDir=font-betas/work-in-progress
++ basename src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace
+ dsName=Recursive-mono-full--w_ital_slnt.designspace
+ fontName=Recursive-mono-full--w_ital_slnt
++ timestamp
++ date +%Y_%m_%d
+ date=2019_08_10
+ fontmake -m src/masters/mono/recursive-varfontprep-2019_08_10-14_22_55/Recursive-mono-full--w_ital_slnt.designspace -o variable --output-path font-betas/work-in-progress/Recursive-mono-full--w_ital_slnt-2019_08_10.ttf
INFO:fontmake.font_project:Building variable font font-betas/work-in-progress/Recursive-mono-full--w_ital_slnt-2019_08_10.ttf
INFO:ufo2ft:Pre-processing glyphs
INFO:cu2qu.ufo:New spline lengths: 1: 18, 2: 1708, 3: 1139, 4: 596, 5: 135, 6: 79, 7: 20
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualASlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualA
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualBSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualB
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualCSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualC
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoCasualB-support.w.middle
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearASlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearA
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearBSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearB
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearCSlanted
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearC
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Recursive-MonoLinearB-support.w.middle
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualASlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualA
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualBSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualB
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualCSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualC
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoCasualB-support.w.middle
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearASlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearA
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearBSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearB
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearCSlanted
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearC
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building OpenType tables for Recursive-MonoLinearB-support.w.middle
WARNING:ufo2ft.fontInfoData:Underline position not set in UFO, defaulting to UPM * -0.075
WARNING:ufo2ft.fontInfoData:Underline thickness not set in UFO, defaulting to UPM * 0.05
INFO:ufo2ft:Building variable TTF font
INFO:fontTools.varLib:Axes:
[{'default': 800.0,
  'hidden': False,
  'labelNames': {'en': 'Weight'},
  'map': [(300.0, 250.0),
          (400.0, 350.0),
          (500.0, 500.0),
          (600.0, 600.0),
          (700.0, 700.0),
          (800.0, 800.0),
          (900.0, 900.0)],
  'maximum': 900.0,
  'minimum': 300.0,
  'name': 'Weight',
  'tag': 'wght'},
 {'default': 0.0,
  'hidden': False,
  'labelNames': {'en': 'Slant'},
  'map': [],
  'maximum': 0.0,
  'minimum': -15.0,
  'name': 'Slant',
  'tag': 'slnt'},
 {'default': 0.5,
  'hidden': False,
  'labelNames': {'en': 'Italic'},
  'map': [],
  'maximum': 1.0,
  'minimum': 0.0,
  'name': 'Italic',
  'tag': 'ital'},
 {'default': 0.0,
  'hidden': False,
  'labelNames': {'en': 'Expression'},
  'map': [],
  'maximum': 1.0,
  'minimum': 0.0,
  'name': 'Expression',
  'tag': 'XPRN'}]
INFO:fontTools.varLib:Internal master locations:
[{'Expression': 1.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 250.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 250.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 800.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 800.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 900.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 900.0},
 {'Expression': 1.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 500.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 250.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 250.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 800.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 800.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': -15.0, 'Weight': 900.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 900.0},
 {'Expression': 0.0, 'Italic': 0.5, 'Slant': 0.0, 'Weight': 500.0}]
INFO:fontTools.varLib:Internal axis supports:
{'Expression': [0.0, 0.0, 1.0],
 'Italic': [0.0, 0.5, 1.0],
 'Slant': [-15.0, 0.0, 0.0],
 'Weight': [250.0, 800.0, 900.0]}
INFO:fontTools.varLib:Normalized master locations:
[{'Expression': 1.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': -1.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': -1.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 0.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 0.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 1.0},
 {'Expression': 1.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 1.0},
 {'Expression': 1.0,
  'Italic': 0.0,
  'Slant': 0.0,
  'Weight': -0.5454545454545454},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': -1.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': -1.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 0.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 0.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': -1.0, 'Weight': 1.0},
 {'Expression': 0.0, 'Italic': 0.0, 'Slant': 0.0, 'Weight': 1.0},
 {'Expression': 0.0,
  'Italic': 0.0,
  'Slant': 0.0,
  'Weight': -0.5454545454545454}]
INFO:fontTools.varLib:Index of base master: 10
INFO:fontTools.varLib:Building variable font
INFO:fontTools.varLib:Loading master fonts
INFO:fontTools.varLib:Generating fvar
INFO:fontTools.varLib:Generating avar
INFO:fontTools.varLib:Building variations tables
INFO:fontTools.varLib:Generating MVAR
INFO:fontTools.varLib:  xhgt: OS/2.sxHeight     [526, 526, 540, 540, 550, 550, 526, 526, 540, 540, 550, 550]
INFO:fontTools.varLib:  stro: OS/2.yStrikeoutPosition   [316, 316, 324, 324, 330, 330, 316, 316, 324, 324, 330, 330]
INFO:fontTools.varLib:  sbxo: OS/2.ySubscriptXOffset    [-19, 0, -19, 0, -19, 0, -19, 0, -19, 0, -19, 0]
INFO:fontTools.varLib:  spxo: OS/2.ySuperscriptXOffset  [88, 0, 88, 0, 88, 0, 88, 0, 88, 0, 88, 0]
INFO:fontTools.varLib:  hcrs: hhea.caretSlopeRise       [1000, 1, 1000, 1, 1000, 1, 1000, 1, 1000, 1, 1000, 1]
INFO:fontTools.varLib:  hcrn: hhea.caretSlopeRun        [250, 0, 250, 0, 250, 0, 250, 0, 250, 0, 250, 0]
INFO:fontTools.varLib:  undo: post.underlinePosition    [-75, -200, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75]
INFO:fontTools.varLib:Generating HVAR
INFO:fontTools.varLib:Merging OpenType Layout tables
Traceback (most recent call last):
  File "/Users/stephennixon/type-repos/recursive/venv/bin/fontmake", line 10, in <module>
    sys.exit(main())
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/__main__.py", line 407, in main
    project.run_from_designspace(designspace_path, **args)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 914, in run_from_designspace
    **kwargs
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 969, in _run_from_designspace_interpolatable
    designspace, output_path=output_path, output_dir=output_dir, **kwargs
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontmake/font_project.py", line 411, in build_variable_font
    inplace=True,
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/ufo2ft/__init__.py", line 548, in compileVariableTTF
    ttfDesignSpace, exclude=excludeVariationTables, optimize=optimizeGvar
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/__init__.py", line 954, in build
    _merge_OTL(vf, model, master_fonts, axisTags)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/__init__.py", line 687, in _merge_OTL
    merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 111, in mergeTables
    for m in master_ttfs])
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 99, in mergeThings
    self.mergeLists(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 85, in mergeLists
    self.mergeThings(value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 97, in mergeThings
    self.mergeObjects(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 76, in mergeObjects
    mergerFunc(self, value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 99, in mergeThings
    self.mergeLists(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 85, in mergeLists
    self.mergeThings(value, values)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 1003, in mergeThings
    super(VariationMerger, self).mergeThings(out, lst)
  File "/Users/stephennixon/type-repos/recursive/venv/lib/python3.7/site-packages/fontTools/varLib/merger.py", line 101, in mergeThings
    assert allEqualTo(out, lst), (out, lst)
AssertionError: (('ringacute', ('gravecomb.case', 'ringacute', 'gravecomb.case', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute', 'ringacute')), 'str', '[12]', 'list', '.glyphs', 'Coverage', '[2]', 'list', '.Coverage', 'MarkGlyphSetsDef', '.MarkGlyphSetsDef', 'GDEF', '.table', 'table_G_D_E_F_')

Assign unicodes to superiors, inferiors, and fractions

Getting these from IBM Plex Mono Bold with this script:

import pprint

font = CurrentFont()

glyphs = font.selectedGlyphNames

glyphUnicodes = {}

for name in glyphs:
    glyphUnicodes[name] = font[name].unicodes
    
pp = pprint.PrettyPrinter(width=80, compact=False)
    
pp.pprint(glyphUnicodes)
{'eightinferior': (8328,),
 'eightsuperior': (8312,),
 'fiveinferior': (8325,),
 'fivesuperior': (8309,),
 'fourinferior': (8324,),
 'foursuperior': (8308,),
 'nineinferior': (8329,),
 'ninesuperior': (8313,),
 'onehalf': (189,),
 'oneinferior': (8321,),
 'onequarter': (188,),
 'onesuperior': (185,),
 'seveninferior': (8327,),
 'sevensuperior': (8311,),
 'sixinferior': (8326,),
 'sixsuperior': (8310,),
 'threeinferior': (8323,),
 'threequarters': (190,),
 'threesuperior': (179,),
 'twoinferior': (8322,),
 'twosuperior': (178,),
 'uni2150': (8528,),
 'uni2151': (8529,),
 'uni2153': (8531,),
 'uni2154': (8532,),
 'uni2155': (8533,),
 'uni2156': (8534,),
 'uni2157': (8535,),
 'uni2158': (8536,),
 'uni2159': (8537,),
 'uni215A': (8538,),
 'uni215B': (8539,),
 'uni215C': (8540,),
 'uni215D': (8541,),
 'uni215E': (8542,),
 'zeroinferior': (8320,),
 'zerosuperior': (8304,)}

Add support layers where needed

Support layers can provide additional control for intermediate masters, between sources. The main problem this solves is contrast at intermediate weights. Strokes are meant to appear uniform in thickness from Light to Medium weights but require a lot of modulation at bolder weights to preserve counters. This means that, if simply interpolated between Light and ExtraBold masters, several more-complex glyph shapes suffer from too much stroke contrast at regular and medium weights.

I've tested this in the /w at wght=500 for Casual and Linear masters. It builds and works well! This image shows the font with this support layer for the /w only:

image

image

image

image

However, there are other glyphs which will also benefit from support layers. This issue will be a list of them.

  • /w in Casual Slanted and Linear Slanted masters
  • /w.italic
  • /m
  • /s
  • /e
  • /a
  • /z
  • /W
  • /M
  • /eight

Unanswered questions about support layers

How will they work with accents? Will I need to build source glyphs for all diacritics, e.g. /aacute, /zcaron, etc, in order to bring these fixes into those glyphs? If so, is it worth the filesize, maintenance burden, and overall complexity?

Optical size naming: Text & Display? Text & Header?

Probably "Text" and "Display," simply because those are the words that are most commonly used for optical sizing descriptions, and to prevent people from taking them too literally (e.g. "header" only ever being used for h1, h2, etc ... if that would ever happen).

QA script: check that code ligature widths are consistent across all masters

This is started in src/00-recursive-scripts-for-robofont/check-for-nonconforming-widths.py

In Mono:

  • all widths should be in units of 600 (code ligatures may be 1200 or 1800, etc)
  • each glyph must be consistent across masters
  • code ligatures should have widths that correspond to their names – (len(underscores) + 1) * 600. For example, numbersign_numbersign_numbersign.code must be 1800 in all masters.

In Sans:

  • all widths should be in units of 50
  • ach glyph must be consistent across masters
  • all kerning in units of 25, consistent between all masters (part of #91)

What is the best way to implement code ligatures? With dlig or calt? Same or separate font?

Code ligatures are not always readable to people who are unfamiliar with them. They also add a fair number of glyphs and coordinates to a font, and therefore add a significant amount of data to final font files.

We should be sure that:

  • Code ligatures are not on by default
  • In web fonts served by Google Fonts, code ligatures are split into an extended Unicode range, rather than served with the initial, basic Latin charset (google/fonts#2103) are not served if users do not want to use that feature
  • If there are specific Recursive Sans font files, code ligatures are subset out.

Look at /percent interpolation from extrabold to heavy masters

There are some glyphs which we'll need for the specimen, aside from the core latin alphabet, that will be really vital to make work this weekend. In a pinch, these can work even just in either sans or mono (we don't necessarily need to bake it all into a full font with the proportion axis). Many of these simply need to be copied over to slanted masters, then corrected. To start, let's make these work in the Mono fonts. Then, Stephen will copy things over to the Sans and make things work over there (e.g. for punctuation, Stephen will copy & correct spacing).

  • space
  • at (to start, just copy the uprights to the slanted familys, skew, recenter, and mark red – we won't need the italic for this just yet)
  • percent (same approach as /at)
  • comma
  • semicolon
  • colon
  • bar
  • quotesingle, quotedbl
  • quoteleft, quoteright (also check double quotes)
  • endash
  • slash
  • fl (in sans)
  • hyphen
  • lslash (for Rafał's name in credits)
  • ellipsis

Others which seem fine, but we should keep an eye on and fix if they come up:

  • ampersand

Aside from letters (#86), other glyphs can be ignored for now (as far as I know). If something basic seems to be missing, please comment below! I will add them to the list if I find I have missed anything.

Useful scripts for this work:

  • src/00-recursive-scripts-for-robofont/checking-similarity-between-fonts/01-check-num_points-selected_fonts.py
  • src/00-recursive-scripts-for-robofont/checking-similarity-between-fonts/check-for-nonconforming-widths.py

Here's the current readout from 01-check-num_points-selected_fonts.py, for the mono fonts:

Point compatibility check, Recursive Mono (Click to expand)
COMPATIBILITY CHECK: GLYPH CONSISTENCY & POINT COUNTS
​
    📊 Emoji are present to help you spot point number differences more quickly. 
    Specific emoji have no meaning as to which numbers are correct or incorrect.
    The first count will be marked with 🍇, the next with 🍋, and so on.


Checking fonts:
•  Mono Casual A Slanted
•  Mono Casual A
•  Mono Casual B Slanted
•  Mono Casual B
•  Mono Casual C Slanted
•  Mono Casual C
•  Mono Linear A Slanted
•  Mono Linear A
•  Mono Linear B Slanted
•  Mono Linear B
•  Mono Linear C Slanted
•  Mono Linear C


--------------------------------------------------------------

L.noserif

	Is in:
	------------------
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Linear B Slanted


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

Ldot

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

Q.titl

	Is in:
	------------------
	 Mono Casual B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

ampersand.code_experimental

	Is in:
	------------------
	 Mono Casual B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

ampersand_ampersand.code

	Is in:
	------------------
	 Mono Casual B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

apostrophemod

	Is in:
	------------------
	 Mono Casual B
	 Mono Casual C


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

at

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A          |   96 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   96 | 🍇
	Mono Casual C          |   96 | 🍇
	Mono Linear A          |   96 | 🍇
	Mono Linear B          |   96 | 🍇
	Mono Linear C          |   96 | 🍇

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

at

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A          |   96 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   96 | 🍇
	Mono Casual C          |   96 | 🍇
	Mono Linear A          |   96 | 🍇
	Mono Linear B          |   96 | 🍇
	Mono Linear C          |   96 | 🍇

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

at.cap

	Is in:
	------------------
	 Mono Casual A


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

backslash

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

bar

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

braceleft.cap

	Is in:
	------------------
	 Mono Casual B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

brokenbar

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

colon

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

comma

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

dagger

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

daggerdbl

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

dcaron

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Linear A
	 Mono Linear B


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

dcroat

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

divisionslash

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

dollar.lower

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Linear A
	 Mono Linear B


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

dotlessij

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

dotlessj.italic

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   50 | 🍇
	Mono Casual A          |   50 | 🍇
	Mono Casual B Slanted  |   50 | 🍇
	Mono Casual B          |   50 | 🍇
	Mono Casual C Slanted  |   50 | 🍇
	Mono Casual C          |   50 | 🍇
	Mono Linear A Slanted  |   50 | 🍇
	Mono Linear A          |    0 | 🍋
	Mono Linear B Slanted  |   50 | 🍇
	Mono Linear B          |   50 | 🍇
	Mono Linear C Slanted  |   50 | 🍇
	Mono Linear C          |   50 | 🍇


--------------------------------------------------------------

ellipsis

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

emdash

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

endash

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

eng

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

eogonek

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Linear A
	 Mono Linear B


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

eth

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

exclam

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

exclamdown

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

fl

	Is in:
	------------------
	 Mono Casual B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

fl.mono

	Is in:
	------------------
	 Mono Casual B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

g.compact_desc

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted


	Not in:
	------------------
	 Mono Casual B
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

hbar

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

hyphen

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A          |   17 | 🍇
	Mono Casual B          |   14 | 🍋
	Mono Casual C          |   14 | 🍋
	Mono Linear A          |   17 | 🍇
	Mono Linear B          |   17 | 🍇
	Mono Linear C          |   17 | 🍇

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

hyphen

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A          |   17 | 🍇
	Mono Casual B          |   14 | 🍋
	Mono Casual C          |   14 | 🍋
	Mono Linear A          |   17 | 🍇
	Mono Linear B          |   17 | 🍇
	Mono Linear C          |   17 | 🍇

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

hyphensoft

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

i

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   45 | 🍇
	Mono Casual A          |   45 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   45 | 🍇
	Mono Casual C Slanted  |   45 | 🍇
	Mono Casual C          |   45 | 🍇
	Mono Linear A Slanted  |   45 | 🍇
	Mono Linear A          |   45 | 🍇
	Mono Linear B Slanted  |   45 | 🍇
	Mono Linear B          |   45 | 🍇
	Mono Linear C Slanted  |   45 | 🍇
	Mono Linear C          |   45 | 🍇


--------------------------------------------------------------

i.italic

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   58 | 🍇
	Mono Casual A          |   58 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   58 | 🍇
	Mono Casual C Slanted  |   58 | 🍇
	Mono Casual C          |   58 | 🍇
	Mono Linear A Slanted  |   58 | 🍇
	Mono Linear A          |   58 | 🍇
	Mono Linear B Slanted  |   58 | 🍇
	Mono Linear B          |   58 | 🍇
	Mono Linear C Slanted  |   58 | 🍇
	Mono Linear C          |   58 | 🍇


--------------------------------------------------------------

i.mono

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   55 | 🍇
	Mono Casual A          |   55 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   55 | 🍇
	Mono Casual C Slanted  |   55 | 🍇
	Mono Casual C          |   55 | 🍇
	Mono Linear A Slanted  |   55 | 🍇
	Mono Linear A          |   55 | 🍇
	Mono Linear B Slanted  |   55 | 🍇
	Mono Linear B          |   55 | 🍇
	Mono Linear C Slanted  |   55 | 🍇
	Mono Linear C          |   55 | 🍇


--------------------------------------------------------------

ij

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

ijacute

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

j

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   58 | 🍇
	Mono Casual A          |   58 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   58 | 🍇
	Mono Casual C Slanted  |   58 | 🍇
	Mono Casual C          |   58 | 🍇
	Mono Linear A Slanted  |   58 | 🍇
	Mono Linear A          |   58 | 🍇
	Mono Linear B Slanted  |   58 | 🍇
	Mono Linear B          |   58 | 🍇
	Mono Linear C Slanted  |   58 | 🍇
	Mono Linear C          |   58 | 🍇


--------------------------------------------------------------

j.italic

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   64 | 🍇
	Mono Casual A          |   64 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   64 | 🍇
	Mono Casual C Slanted  |   64 | 🍇
	Mono Casual C          |   64 | 🍇
	Mono Linear A Slanted  |   64 | 🍇
	Mono Linear A          |   64 | 🍇
	Mono Linear B Slanted  |   64 | 🍇
	Mono Linear B          |   64 | 🍇
	Mono Linear C Slanted  |   64 | 🍇
	Mono Linear C          |   64 | 🍇


--------------------------------------------------------------

kgreenlandic

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

lcaron

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Linear A
	 Mono Linear B


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

ldot

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

lslash

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

mu.math

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

nbspace

	Is in:
	------------------
	 Mono Linear B


	Not in:
	------------------
	 Mono Casual A
	 Mono Casual A Slanted
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted

--------------------------------------------------------------

nhookleft

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

numbersign

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

oe

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

partialdiff

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

percent

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A          |   80 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   80 | 🍇
	Mono Casual C          |   80 | 🍇
	Mono Linear A          |   80 | 🍇
	Mono Linear B          |   80 | 🍇
	Mono Linear C          |   80 | 🍇

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

percent

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A          |   80 | 🍇
	Mono Casual B Slanted  |    0 | 🍋
	Mono Casual B          |   80 | 🍇
	Mono Casual C          |   80 | 🍇
	Mono Linear A          |   80 | 🍇
	Mono Linear B          |   80 | 🍇
	Mono Linear C          |   80 | 🍇

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

quotedbl

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

quotedblbase

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

quoteleft

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

quoteright

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

quotesinglbase

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

quotesingle

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

schwa

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

section

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   99 | 🍇
	Mono Casual A          |  101 | 🍋
	Mono Casual B Slanted  |   99 | 🍇
	Mono Casual B          |   99 | 🍇
	Mono Casual C Slanted  |   99 | 🍇
	Mono Casual C          |   99 | 🍇
	Mono Linear A Slanted  |   99 | 🍇
	Mono Linear A          |   99 | 🍇
	Mono Linear B Slanted  |   99 | 🍇
	Mono Linear B          |   99 | 🍇
	Mono Linear C Slanted  |   99 | 🍇
	Mono Linear C          |   99 | 🍇


--------------------------------------------------------------

semicolon

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

slash

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual B Slanted
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

space

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Casual C Slanted
	 Mono Linear A
	 Mono Linear A Slanted
	 Mono Linear B
	 Mono Linear B Slanted
	 Mono Linear C
	 Mono Linear C Slanted


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted

--------------------------------------------------------------

summation

	Font                   |  Pts | 📊
	---------------------- | ---- | --
	Mono Casual A Slanted  |   51 | 🍇
	Mono Casual A          |   51 | 🍇
	Mono Casual B Slanted  |   60 | 🍋
	Mono Casual B          |   60 | 🍋
	Mono Casual C Slanted  |   60 | 🍋
	Mono Casual C          |   60 | 🍋
	Mono Linear A Slanted  |   60 | 🍋
	Mono Linear A          |   60 | 🍋
	Mono Linear B Slanted  |   60 | 🍋
	Mono Linear B          |   60 | 🍋
	Mono Linear C Slanted  |   60 | 🍋
	Mono Linear C          |   60 | 🍋


--------------------------------------------------------------

tbar

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted

--------------------------------------------------------------

thorn

	Is in:
	------------------
	 Mono Casual A
	 Mono Casual B
	 Mono Casual C
	 Mono Linear A
	 Mono Linear B
	 Mono Linear C


	Not in:
	------------------
	 Mono Casual A Slanted
	 Mono Casual B Slanted
	 Mono Casual C Slanted
	 Mono Linear A Slanted
	 Mono Linear B Slanted
	 Mono Linear C Slanted
️

Glyphs with unequal points between fonts:
	 at dotlessj.italic hyphen i i.italic i.mono j j.italic percent section summation 


Glyphs that aren't in all fonts:
	 L.noserif Ldot Q.titl ampersand.code_experimental ampersand_ampersand.code apostrophemod at at.cap backslash bar braceleft.cap brokenbar colon comma dagger daggerdbl dcaron dcroat divisionslash dollar.lower dotlessij ellipsis emdash endash eng eogonek eth exclam exclamdown fl fl.mono g.compact_desc hbar hyphen hyphensoft ij ijacute kgreenlandic lcaron ldot lslash mu.math nbspace nhookleft numbersign oe partialdiff percent quotedbl quotedblbase quoteleft quoteright quotesinglbase quotesingle schwa semicolon slash space tbar thorn 

--------------------------------------------------------------

NOTE: ignored the following glyphs (they are experiments or currently low-priority)
(edit script to adjust these)
R.trap Z.noserif ampersand.code_experimental_2 ampersand.code_experimental_3 ampersand.crossbar at.simple at.short at.replaced_with_prop dagger.daggery g.extra g.long_desc g.longtail g.ss01 onehalf.v1 perthousand r.simple_italic sterling.replaced_with_flat two.replaced_with_rounder zero.ss01 one.flatflag 

</details>

Commonly confused glyph pairs in source code with Mono builds

Stephen and Katja-

Beautiful typeface! Love this and look forward to seeing it develop.

I took a look at a handful of commonly confused glyphs in source code using this specimen in the Codeface repository that was collaboratively developed by a number of software developers.

I captured screenshots of that text specimen as dark on light and light on dark with syntax highlighting activated and laid this out in text editors on macOS and Windows 10. The images in this thread are from Win 10 VS Code and are included in this archive:

recursive-mono.zip

Glyphs that may need to be addressed in order to distinguish the shapes at small sizes:

  • I vs. l
  • S vs. 5
  • B vs. 8

See what you think based on the images.

Notably, the l and 1 look great. This is a common issue in code fonts. :)

My 2¢!


8px

8pt-light

8pt-dark

9px

9pt-light

9pt-dark

10px

10pt-light

10pt-dark

11px

11pt-light

11pt-dark

12px

12pt-dark

12pt-light

13px

13pt-light

13pt-dark

14px

14pt-light

14pt-dark

Measure impact of shared glyph outlines on filesize. Find if filesize can be further reduced.

​​Characters that don’t need to change width to work well in both Mono & Sans styles are shared between them.

I suspect this allows better compression in the resulting woff2 files, but I don't know. I am fairly certain it would reduce filesize in OTF files because in OTF (AKA "CFF," or "compact font format") repeated contours can be stored once and referenced, almost like mini-components. However, I don't think that happens in TTFs.

However, from what I can tell, that is one of the compression techniques in woff2:

WOFF 2.0 adds a preprocessing step, before entropy coding.The reason for this is that many fonts contain redundant or duplicate information, or information that can be deduced from other items of data also present. Data may also be padded to a convenient multiple of 8 bits, because aligned data is faster to access. They are thus optimized for convenience and speed of access at the expense of size. Preprocessing detects and eliminates redundant data, more efficiently encodes derived data, and stores data in the minimal number of bits. Following decompression, a reconstruction step is needed to re-align data and to compute derived values.

– from https://www.w3.org/TR/WOFF20ER/

Digging further, I can't find anything more clear than that. It should be relatively simple to test: I could make one mono–sans VF into a woff2. I could then edit those masters with a script that randomizes points, make that into a new woff2 VF, and compare their sizes.

Make mono-to-sans glyph copier more efficient

Easy wins:

  • double-check that the newItalicGlyphs list is comprehensive
  • select just a mono, and automatically find the matching sans UFO
  • have an (optional) controller script that will just run through all UFOs and copy glyphs
  • don't copy experimental glyphs (anything marked (0.5, 0, 1, 0.75))

Determine characters which should NOT slant for slant axis / italics

Not all glyphs should be slanted in an italic design. This is a place to list them and track them.

Keep upright forms in Slanted Masters:

  • /degree (it's a circle, and needs to stay that way)
  • /numbersign (otherwise, this moves in an annoying way in code syntax themes that slant comments)
  • /currency ? (we may or may not need this slanted – it seems like a character that is simply "round" – almost an icon more than a letter)

Keep slanted forms in upright masters:

  • /florin

Glyphs to fix for beta 2 (incorrect shaping, temporarily-skewed italics, etc)

Slanted versions that are skewed for Beta 1.

  • Correct in Mono and copy in if width of contours doesn't change in Sans

  • percent

  • emdash (fix, copy, widen)

  • perthousand

  • quotesinglbase

  • quotedblbase

  • dagger

  • daggerdbl

  • exclam

  • exclamdown

  • backslash

  • brokenbar

  • partialdiff

  • mu.math

  • ampersand (stephen will do this)

Sans-specfic corrections:

  • AE
  • ae
  • at (stephen will do this)

"Incorrect" shaping

  • Uring shouldn't have a connected ring (can use normal ring)
  • l in Sans light needs a sharper exit stroke, similar to i.italic

Possibly incorrect:

  • lcaron
  • Lcaron
  • Ldot

Needs adjustment in Sans:

  • asterisk (needs to be smaller and raised)
  • Y (probably should be wider, as it is in Linear C Slanted & Casual C Slanted, background layer)
  • Oslash
  • Enj
  • Hbar
  • Nhookleft
  • Eth, Dcroat
  • oe

Letters to polish (stephen will do these):

  • x (too light in heavy masters)
  • x.italic ("leans forward" in upright linear masters, also light in Heavy)
  • numbersign (same lean in upright & slanted, or a change? this may change w/ code ligs)
  • make sure straight and curly quotes are similar size

Check for kinks especially in:

  • s

Anchors:

  • /L has 'slash' in Sans Casual B, but no other fonts. Needs fixing & building.

file issue with browsers: "font-style: italic; currently doesn't work in any major browser"

Jason Pamental wrote about the currently-inconsistent implementation of font-style in browsers:

if you specify ‘font-style: italic’ that should tell the browser to render the font with the Italic axis set to 1 instead of 0. The trouble is, it doesn’t actually work. At least not as you might expect.

https://mailchi.mp/444e7a454775/web-typography-news-20-contextual-stylistics-and-other-fake-band-names

He offers a CodePen demo to show the problem (see in Safari vs Chrome vs Firefox):

https://codepen.io/jpamental/pen/rEKQZq?editors=1100

However, font-style: italic doesn't seem to work for an early version of Recursive, at all (even though font-variation-settings does work as expected).
https://codepen.io/thundernixon/pen/ormaav

Safari - italic almost does what it should, whether or not font-style: oblique 0deg 20deg; is included in the @font-face declaration.:

image

...but it is skewed, rather than using the proper slnt axis. This can be seen most easily in the distortion of the letter O.

Chrome & Firefox – no result from italic or oblique:

image

image

update existing diacritics, copy into slanted masters & correct

There are quite a few diacritics in the upright mono masters of Recursive. These need to get the standard upgrades: removed exterior overlap, inktraps, and slanted versions.

Additionally, we will need to make sure that .italic glyphs have anchors set – most probably don't, at this point.

Important tools:

  • Glyph Construction Extension
  • Anchor Tool Extension

They will need to be built with glyph construction, from refined recipes.

  • collaborator: upgrade existing drawings
  • use Glyph Construction to build diacritics
  • check that accents are where they "should be"
    • in particular, check that width-adjusted glyphs have properly-centered middle anchors in Sans masters
    • check that ogonek anchors are 1 unit away from the anchor they need to connect with.
Ogonek anchor position (Click to expand)

image

image

  • be sure that the /grave does not use a component of /gravecomb. In a coding font, the /grave is used in JavaScript syntax similar to the /quote, so it should be more similar to that size. I think it looks best when
    • it goes from about the x-height to the top of the normal /gravecomb
    • has about the same slant as the /gravecomb
    • has the same color and apparent size as the /quote
    • is a bit to the left-of-center, so it looks approximately-centered with control characters
/grave in Linear B (Click to expand)

image

Stephen will work in #95 for glyph construction and QA.

Workflow

  1. Open Glyph Construction
  2. Copy-paste in code from src/00-recursive-scripts-for-robofont/diacritics-and-glyph_construction-recipes/diacritic-recipes-for-recursive.txt
  3. Click "Analyze," and scroll through the preview of built glyphs, visually scanning for anything that looks off-center or too high/low, etc.
  4. If you find something to fix, fix the anchor in the base glyph. NOTE: if you are working in a Sans master and the base glyph has a gray marking, it is copied from a mono master. Fix the problem there, and it will be copied in later.
  5. If you find an issue in diacritic-recipes-for-recursive.txt, fix this in the original file, then copy-paste this back in Glyph Construction. I find Glyph Construction useful, but slightly buggy – it will crash RoboFont relatively often. So, it's best to work on the file in a code editor instead.
  6. Save your UFOs often, as well, while using Glyph Construction.
  7. Periodically run the script src/00-recursive-scripts-for-robofont/check-anchor-similarity-selected-fonts.py to select fonts to check for consistency of anchors across them. Ultimately, we need completely consistent anchors across all sources (mono & sans) for all base glyphs.
Glyph Contstruction screenshot (Click to expand)

image

Simplify outlines in lowercase .italic glyphs

Originally, glyphs were drawn with a lot of overlaps – one for every "brush stroke." This was cool but caused rending problems on screens. As an additional problem, it was quite difficult to keep compatible between all masters.

We simplified to eliminate "exterior overlaps," but used separate contours for the flat parts of inktraps, still following the general idea of a "brush stroke" derived typeface. This solved the rendering issues, but was hard to keep compatible between masters, and these inktraps sometimes would disappear in interpolated instances.

We eventually moved into simpler outlines, and these proved to be the most efficient to work with, and the most predictable between masters, and a good balance of flexibility and simplicity. This started in the basic Latin uppercase letters.

Recently, most of the default lowercase has been edited to have this simpler system of overlapping contours. This will make them much nicer to work with as we make the 24 total mono & sans masters compatible.

Here's a screen recording of an efficient approach to this work: https://www.youtube.com/watch?v=6Yf6nR_Hv34

Below are the glyphs that should be simplified.

Do these as soon as possible, to make it possible to edit Sans versions without wasting time:

  • d.italic
  • g.italic
  • i.italic
  • j.italic
  • m.italic
  • r.italic
  • w.italic
  • z.italic

Also good to do, but these are simply copied from mono to sans, so fixes could also be made further in the future, then copied in.

  • x.italic
  • u.italic
  • n.italic
  • k.italic
  • h.italic
  • a.italic
  • e.italic
  • v.italic
  • y.italic

Fix compatibility in Mono masters for new VF build

We need to do a cycle of compatibility-matching to make things build in a better way.

Glyphs that aren't compatible, caught by FontParts isCompatible() check, so probably have more-obvious issues:

    'asterisk', 'r.italic', 'question', 'k.italic', 'parenright', 'ringacute', 'dotlessi', 'dotlessj', 'dotbelowcomb.case', 'macron', 'underscore'

Incompatibility caught by cu2qu

    cu2qu.errors.IncompatibleFontsError: fonts contains incompatible glyphs:
    'E', 'L', 'Lslash', 'OE', 'ampersand', 'asciicircum', 'circumflexcomb', 'e', 'g.italic', 'g.mono', 'greaterequal', 'h', 'i', 'increment', 'j', 'less', 'lessequal', 'logicalnot', 'lozenge', 'ordfeminine', 'radical', 'trademark', 'u', 'v', 'x'
cu2qu incompatibility output messages (Click to expand)
INFO:ufo2ft:Pre-processing glyphs
ERROR:cu2qu.ufo:Glyphs named 'trademark' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'Lslash' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'g.italic' have incompatible segment types:
  19: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'less' have incompatible segment types:
  7: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
  8: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'h' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'logicalnot' have incompatible segment types:
  5: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line')
  8: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'e' have incompatible segment types:
  17: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'ordfeminine' have incompatible segment types:
  24: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line')
ERROR:cu2qu.ufo:Glyphs named 'greaterequal' have incompatible segment types:
  14: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
  15: ('curve', 'curve', 'curve', 'line', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'lessequal' have incompatible segment types:
  14: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
  15: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'radical' have incompatible segment types:
  13: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
  14: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'lozenge' have incompatible segment types:
  17: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
  18: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
  22: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
  23: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'OE' have incompatible segment types:
  13: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve', 'line')
  14: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'circumflexcomb' have incompatible segment types:
  7: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve', 'curve', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'asciicircum' have incompatible segment types:
  7: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
  8: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'E' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'L' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'v' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'j' have incompatible segment types:
  12: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve', 'line', 'curve', 'curve')
  13: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve', 'line', 'curve', 'line', 'line')
  17: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve', 'line', 'curve', 'curve')
  18: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve', 'line', 'curve', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'increment' have incompatible segment types:
  12: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'line', 'line', 'line', 'line', 'line')
  13: ('line', 'line', 'line', 'line', 'line', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
ERROR:cu2qu.ufo:Glyphs named 'u' have incompatible segment types:
  5: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve', 'line', 'curve', 'curve')
  7: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve', 'line', 'curve', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'x' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'g.mono' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'ampersand' have incompatible segment types:
  39: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve', 'curve', 'curve', 'curve')
  41: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve', 'line', 'line', 'line', 'line')
  42: ('curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'line', 'curve', 'curve', 'curve', 'curve')
  43: ('line', 'line', 'line', 'line', 'line', 'line', 'line', 'curve', 'line', 'line', 'line', 'line')
ERROR:cu2qu.ufo:Glyphs named 'w' have different number of segments
ERROR:cu2qu.ufo:Glyphs named 'i' have incompatible segment types:
  15: ('curve', 'line', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve', 'curve')
  18: ('line', 'curve', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line', 'line')

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.