Skip to content

Instantly share code, notes, and snippets.

@m4rc1e

m4rc1e/gf.md Secret

Last active April 28, 2020 07:55
Show Gist options
  • Save m4rc1e/8f4c4498519e8a36cd54e16a004275cb to your computer and use it in GitHub Desktop.
Save m4rc1e/8f4c4498519e8a36cd54e16a004275cb to your computer and use it in GitHub Desktop.
Google Fonts: Documentation

Google Fonts Docs (WIP)

Contents

Getting started

Contributing

Universal

Static Fonts

Variable Fonts

The google/fonts repo

QA

FAQ

TODO

Getting started

Prerequisite knowledge

We expect font developers to understand the following:

  • Basic understanding of how OpenType fonts work.
  • A commercial font editor such as Fontlab VI, Glyphsapp, Robofont or Fontforge
  • Version control, specifically git. Use of desktop clients such as Github desktop is acceptable
  • Shell scripting
  • Managing python packages/tools using pip

The following resources should bring you up to speed.

Tooling

Google Fonts has its own tooling which will help you master and check your families. All our tools are written in Python.

Our tools can be installed using the following terminal commands:

pip install gftools
pip install fontbakery
pip install fontmake

We recommend installing our tools inside a Python virtualenv.

Scalable font production

Google Fonts is used in approximately a third of all websites (citation needed, it may be more). We treat fonts with the same level of care as we do with software. We must ensure updated fonts do not break existing documents or websites. This requires treating fonts as software. We abide by the following principles:

  • Font builds are repeatable
  • Fonts can be built in one step
  • Fonts can be built on any platform since we use opensource tools
  • Projects are kept in version control
  • We fix issues before upgrading
  • We use CI for build and testing purposes
  • We have testers with font domain knowledge

How we achieve this

  • All our font production tools can be run from the commandline. This allows us to write shell scripts to generate font families.
  • We use fontmake to build our fonts.
  • If we need to post process generated fonts, we use our gftools fix scripts.
  • All of our tools are written in Python. We distribute these tools using pypi/pip. This allows us to use specific versions of each package. This ensures we're able to get the same results for each build.
  • Since we only release Open Source fonts, we expect every family we release to have its own upstream github repository.
  • Our main repository github.com/google/fonts uses CI. When new fonts are pushed, the CI will run our test suite and the results will be reviewed.

Font developers may find the above requirements overkill but our scale requires them. Joel Spolsky's classic article The 12 steps to better code illustrates why these requirements are beneficial.

Contributing

Creating a new family

  • Font Design is original and of high quality
  • Fonts and sources are available on Github
  • There should only be one set of sources
  • Fonts are built using fontmake and can be built in one step

Updating an existing family

  • Your modifications must be merged into the upstream project's github repository
  • Your modifications must be made to the existing sources.
  • No glyphs should be missing
  • No styles should be missing
  • Visual regressions must be avoided as much as possible.

TODO MF: expand this section

Universal

The following applies to all fonts.

Font copyright

Copright strings should be based on the following schema:

Copyright { year } The { family } Project Authors ({ git_url })

e.g

Copyright 2019 The Oswald Project Authors (https://www.github.com/googlefonts/Oswald)

If the project is OFL licensed, the first line of the OFL.txt file should be identical to the font copyright string.

Project authors can be specified in an AUTHORS.txt file. Project contributors can be specified in a CONTRIBUTERS.txt file. Please duplicate and modify these files instead of reinventing the wheel.

Font versioning

Versioning is based on semver, apart from we use MAJOR.SIGNIFICANTMINORPATCH, instead of MAJOR.MINOR.PATCH.

Examples:

If a breaking change is made e.g converting a static font family to a variable font family, the MAJOR must be incremented by 1 and the others reset e.g

Current 1.230, new 2.000

If a new character set is inserted, SIGNIFICANT should be incremented e.g:

Current 1.230, new 1.330

If a few new glyphs are added, MINOR should be incremented e.g:

Current 1.230, new 1.240

If a name table record is updated such as the copyright string, PATCH should be incremented e.g:

Current 1.230, new 1.231

Font Embedding (fsType)

Set to 0 (Installable embedding)

Font Vertical Metrics

TODO

CJK Vertical Metrics

CJK vertical metrics are based on Source Han Sans and Noto CJK fonts.

The following vertical metric values must be applied to all CJK fonts

Attrib Value Example using 1000upm font
OS/2.sTypoAscender 0.88 * font upm 880
OS/2.sTypoDescender -0.12 * font upm -120
OS/2.sTypoLineGap 0 0
hhea.ascender Set to look comfortable (~1.16 * upm) 1160
hhea.descender Set to look comfortable (~0.288 * upm) -288
hhea.lineGap 0 0
OS/2.usWinAscent Same as hhea.ascent 1160
OS/2.usWinDescent abs(value) of hhea.descent 288
OS/2.fsSelection bit 7 Do not set

Our decision to follow the Adobe schema was based on dr Ken Lunde's comments and his release notes on Source Han Sans

Monospace fonts

We require the OS/2 panose and post table isFixedPitch to be set. If neither of these are set, users may get fallback glyphs which are not monospaced, if they type a character which doesn't exist in the font.

Set OS/2.panose.bProportion to 9 and post.isFixedPitch to 1.

Developers can set these automatically by using the following gftools command:

gftools fix-isfixedpitch

CJK Fonts

CJK fonts have the following additional requirements:

Static Fonts

Static font filenames

Font filenames must be based on the following schema:

FamilyName-Style.ttf e.g Montserrat-Regular.ttf

The filename must not contain anything else.

Supported Styles

The static fonts api supports 18 styles which consist of 9 weights, Thin-Black + Italics. The table below lists each style its font specific settings. TODO MF convert integers to bits

Filename Family Name (nameID 1) Subfamily Name (nameID 2) Typographic Family Name (nameID 16) Typo Subfamily Name (nameID 17) OS/2.usWeightClass OS/2.fsSelection hhea.macStyle
FamilyName-Thin.ttf Family Name Thin Regular Family Name Thin 100 64 0
FamilyName-ExtraLight.ttf Family Name ExtraLight Regular Family Name ExtraLight 200 64 0
FamilyName-Light.ttf Family Name Light Light Family Name Light 300 64 0
FamilyName-Regular.ttf Family Name Regular 400 64 0
FamilyName-Medium.ttf Family Name Medium Regular Family Name Medium 500 64 0
FamilyName-SemiBold.ttf Family Name SemiBold Regular Family Name SemiBold 600 64 0
FamilyName-Bold.ttf Family Name Bold 700 32 1
FamilyName-ExtraBold.ttf Family Name ExtraBold Family Name ExtraBold 800 64 0
FamilyName-Black.ttf Family Name Black Family Name Black 900 64 0
FamilyName-ThinItalic.ttf Family Name Thin Italic Family Name Thin Italic 100 1 2
FamilyName-ExtraLightItalic.ttf Family Name ExtraLight Italic Family Name ExtraLight Italic 200 1 2
FamilyName-LightItalic.ttf Family Name Light Italic Family Name Light Italic 300 1 2
FamilyName-Italic.ttf Family Name Italic 400 2 2
FamilyName-MediumItalic.ttf Family Name Medium Italic Family Name Medium Italic 500 1 2
FamilyName-SemiBoldItalic.ttf Family Name SemiBold Italic Family Name SemiBold Italic 600 1 2
FamilyName-Bold.ttf Family Name Bold Italic 700 33 3
FamilyName-ExtraBold.ttf Family Name ExtraBold Italic Family Name ExtraBold Italic 800 1 2
FamilyName-Black.ttf Family Name Black Italic Family Name Black Italic 900 1 2

If a family has styles which are not in the above table, they should be released as a new family. To do this is, append the Unsupported style e.g Condensed to the family name. We frequently use this approach for Condensed and smallcap sibling families.

For projects which use glyphsapp, we have an example repository which contains glyphs files that are set correctly.

Single Weight families

If a family is a single weight and it visually doesn't have the appearance of a Regular weight, "One" must be appended to the family name. This approach allows us to keep the desired font name available if we decide to add more styles in the future. We have released many of these families in the past.

The single weight families must have the following font specific settings:

Filename Family Name (nameID 1) Subfamily Name (nameID 2) Typographic Family Name (nameID 16) Typo Subfamily Name (nameID 17) OS/2.usWeightClass OS/2.fsSelection hhea.macStyle
FamilyNameOne-Regular.ttf Family Name One Regular 400 64 0

Hinting

Static fonts should be hinted using the latest version of ttfautohint. If the results look poor on Windows browsers, it's better to release the fonts unhinted. Ttfautohint often struggles to hint display or handwritten typefaces.

Once the fonts have been hinted, run the fonts through gftools fix-hinting. If the fonts are unhinted, run the fonts through gftools fix-nonhinting.

Variable Fonts

Font origin

A variable font is simply a static font which has some additional tables FVAR, GVAR etc These new tables allow text clients to visually alter the font so it has a different appearance to end users. Often font developers are unaware what the font origin is within their fonts. They'll then complain that fontbakery is failing many checks. It is recommended that font developers read the Microsoft OpenType Font Variations Overview to understand how a variable works.

Variable font requirements

Family is new and isn't on Google Fonts

  • VF must not be appended to the family name.
  • Fonts should be unhinted and have gftools fix-nonhinting applied to them
  • The wght axis range must include 400 e.g 100-900, 400-900, 100-400 etc
  • Fonts must conform to the sections listed below

Family already exists on Google Fonts

  • Family name must be the same
  • Hinting should match as closely as possible. If ttfautohint-vf produces bad results, we can release the family unhinted
  • Every style that is available on Google Fonts must be included as an fvar instance
  • There should be no missing glyphs
  • Visual regressions should be as minimal as possible
  • Fonts must conform to the sections listed below

A good rule to consider is that users should be able to swap the old family with the new VFs and not notice any differences.

VF Font filenames

Font filenames must be based on the following schema:

FamilyName[axis1,axis2].ttf e.g Montserrat[wdth,wght].ttf

Axes should be listed in alphabetical order.

If your font contains unregistered axes, they should be capitalised and be listed first.

Montserrat[GOOF,VEST,wdth,wght].ttf

If the family consists of two VFs, one for Italic, the other for Roman. The fonts should named:

Montserrat[axis1,axis2...].ttf
Montserrat-Italic[axis1,axis2...].ttf

Axes

Google Fonts supports all Microsoft registered axes.

We only support the following axes ranges:

wght: 100-1000

wdth: 50-200

opsz: 0-∞

Developers can include their own axes but FontBakery will warn users that we cannot determine if the values are correct.

Instances

Instance names and fvar coordinates must relate to the following tables.

wght

name wght coordinate value
Thin 100
ExtraLight 200
Light 300
Regular 400
Medium 500
SemiBold 600
Bold 700
ExtraBold 800
Black 900
ExtraBlack 1000

wdth

name wdth coordinate value
UltraCondensed 50
ExtraCondensed 62.5
Condensed 75
SemiCondensed 87.5
100
SemiExpanded 112.5
Expanded 125.0
ExtraExpanded 150.0
UltraExpanded 200.0

opsz

name opsz coordinate value
XXpt XX

XX can be any value

Instance names

Instance names must mention the axes in the following order:

opsz wdth wght

e.g 10pt Expanded Regular

The order of the axes cannot be changed e.g Regular Expanded 10pt is not valid.

If a font doesn't include an axis which is listed above, it is simply skipped but the order kept e.g 10pt Regular.

STAT Table

All variable fonts should contain a STAT table (style attributes table). This table makes sure that instances are selectable in DTP apps. We recommend reading the MS Spec.

We use DaMa statmake to generate this table. Marc Foley has written a python helper script that will generate the STAT table for Inconsolata. TODO (M Foley) may be worth making a more general STAT table generator or include templates that can be altered. The prerequisite knowledge section is fairly advanced due to these types of requirements.

VF Hinting

Variable Font hinting still doesn't have a clear policy. Marc Foley has been applying the following pattern:

Family already exists on Google Fonts

If the family has over a billion weekly views, use VTT. TODO Marc Foley. Write more about this. Also include example to Inconsolata once it is finished.

If the family has under a billion weekly views, try ttfautohint-vf. If the results are bad, release unhinted.

If the font has been hinted using VTT or ttfautohint-vf, run the fonts through gftools fix-hinting e.g

gftools fix-hinting Montserrat[wght].ttf

Family does not exist on Google Fonts

Release unhinted. Run fonts through gftools fix-nonhinting e.g

gftools fix-nonhinting Savant[wght].ttf

The google/fonts repo

The google/fonts repository is used as a staging area which allows font developers to upload their families to our website, Google Fonts. When font developers push families, they are reviewed by a member of the team. If the family meets our quality criteria, the family will be pushed into production. We aim to push families into production every fortnight.

Repository structure

The repository has the following structure:

.
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── README.md
├── TRIVIA.md
├── apache
├── catalog
├── ofl
├── to_production.txt
├── to_sandbox.txt
├── tools
└── ufl

The ofl, ufl and apache directories contain font families which we refer to as family dirs. Each family dir has the following structure:

.
├── DESCRIPTION.en_us.html
├── FONTLOG.txt
├── METADATA.pb
├── License (OFL.txt, UFL.txt, License.txt)
├── FontFamily-Regular.ttf

If the family is a variable font family, another directory called "static must be included. This directory contains static fonts for the family.

Each file has the following purpose:

  • DESCRIPTION.en_us.html: describes the font family
  • FONTLOG.txt: (optional) lists all the changes which have happened to the family
  • METADATA.pb: contains metadata related to the family
  • License: License for the font family. Valid choices are OFL.txt, UFL.txt, License.txt. If you're unsure what license to use, we recommend OFL.txt
  • *.ttfs: Family font files.

QA

The quality assurance process if fairly strict in comparison most foundries and for good reasons. The Google Fonts font apis are non-versioned. This means that all users will recieve the same fonts. If we make an update to an existing family and radically alter it, it will affect existing users. The following general rules apply:

Family is already on Google Fonts

  • Family name must be the same
  • No missing encoded glyphs
  • No missing styles/instances
  • Vertical metrics must have the same visual appearance to end users

Family is not on Google Fonts

TODO M Foley, include these requirements as individual sections.

Our process

TODO

FAQ

Fontbakery keeps reporting that my masters are not named correctly?

If the source file is .glyphs, ...

If the source file is .designspace. Inspect the designspace and see if the Axis map elements are set correctly

Fontbakery keeps reporting that the usWeightClass is incorrect?

Are

@arrowtype
Copy link

Awesome idea to put this together!

One nitpick: I believe the allowed wght range is actually 1–1000, as the CSS Spec now allows. See this codepen for a test of wght=1000 working in Recursive on the GF sandbox.

image

If Hepta Slab VF has made it onto the sandbox (it is at google/fonts/ofl/heptaslab, that could help test whether wght=1 is supported.

We may also be able to decide upon names to standardize on, even before CSS establishes an official convention (it looks like they only specify names for 100–900, currently).

In Recursive, I called wght 1000 ExtraBlack after talking it over with Nyshadt (and I think Dave, though I'm not sure?). We could also call it something like "Ultra" or "UltraBlack," but those may not be as straightforward.

In Hepta Slab, wght=1 is called Hairline. I agree that this is the most obvious and common name for extremely-thin weights.

@m4rc1e
Copy link
Author

m4rc1e commented Feb 18, 2020

Thank you for writing this!

In Recursive, I called wght 1000 ExtraBlack after talking it over with Nyshadt (and I think Dave, though I'm not sure?). We could also call it something like "Ultra" or "UltraBlack," but those may not be as straightforward.

I'll update the Doc so 1000 ExtraBlack is included.

@arrowtype
Copy link

Thanks!

I believe we should also have wght=1, hairline.

@m4rc1e
Copy link
Author

m4rc1e commented Feb 18, 2020

Perhaps we should allow users the freedom to choose their own weight names for ranges 0-99 and 901-1000? I'll run the idea by GF eng.

@graphicore
Copy link

Perhaps for Font copyright it would make sense to mention AUTHORS.txt in the projects git, as a place to add the actual copyright holders.

@thundernixon
Copy link

Two questions have come up at google/fonts#2354 that I don't know how to answer very confidently and which will probably be questions in many variable font projects:

  1. What is our recommendation for hinting? Do we recommend TTFautohint? If so, what version? Is there any recommendation we can make for how to find the best settings for different fonts?

  2. Do we have any recommendations for how to create accurate STAT tables in multi-axis fonts,? Is there anything we can say to clarify this subject? The MS OpenType spec for this is one of the more dense & confusing specs. Do we recommend statmake? What format of the STAT table should people use? Does FontMake ever take care of this, or is it something that has to be specifically addressed in each project?

@m4rc1e
Copy link
Author

m4rc1e commented Feb 21, 2020

Here's my suggestions:

  1. If the family has over 1 billion views, we should vtt hint it, otherwise we should release it unhinted. We will have a more concrete answer soon.

2, We should be using dama statmake.

I've included both questions in the docs. Thanks and keep the questions coming. This is great.

@thundernixon
Copy link

Ahh nice, the inconsolata gen_stat.py script is a way nicer way to make a statmake plist than trying to write it out by hand.

Am I misunderstanding it to think that it would work with any variable font, if fed TAG_TO_NAME and file args? Something like...

gftools genStatmake wght=Weight wdth=Width MONO=Monospace <font_path>

I guess maybe even if that would work, people would still need to set up statemake, though

@kontur
Copy link

kontur commented Feb 25, 2020

The axis to name association should not be necessary since the fvar table has the axis name IDs. What would be more interesting is how to automate creating axis locations, which is really what is needed for Windows desktop apps to render some instance names in the font menu. I stated a discussion in the statmake repo, but maybe (ideally) this would be something fontTools does (beyond just dumping the axes, which, I believe, it already does).

@m4rc1e
Copy link
Author

m4rc1e commented Feb 25, 2020

Agree with @kontur on not needing an axis to name mapping if we did decide to make a helper script.

It would also be great to have single vfs with 1 axis generate a correct STAT as twardoch suggested.

@arrowtype
Copy link

arrowtype commented Apr 27, 2020

This doc says:

Google Fonts supports all Microsoft registered axes.

We only support the following axes ranges:

wght: 100-1000

Why not wght 1–1000, as the OpenType spec details?

Valid numeric range: Values must be in the range 1 to 1000.

@m4rc1e
Copy link
Author

m4rc1e commented Apr 28, 2020

@arrowtype This doc is now deprecated. It has been moved to gf docs, https://github.com/googlefonts/gf-docs/tree/master/Spec#variable-font-requirements

I'll add 1 and 50 soon. The only issue is these weights conflict with our "multiples of 100 check"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment