material-icons / build

2 stars 1 forks source link

find a way to generate fonts like google does #1

Open jossef opened 5 years ago

cyberalien commented 5 years ago

Metrics that are used by Google's version of font that matter:

unitsPerEm: 512 ascent: 512 descent: 0 lineGap: 0

Problem with FontForge is when it generates font in TTF, WOFF or WOFF2 format, it changes ascent and lineGap values. For TTF it sets ascent to 469, for WOFF to 470.

Simple way to test generated metrics:

const fontkit = require('fontkit');
let font = fontkit.openSync('font.woff');
['ascent', 'descent', 'lineGap'].forEach(attr => console.log(attr, font[attr]));

So far I had success with this process:

  1. Generate font with FontForge, save it as SVG font
  2. Open SVG font file, replace some values (such as underline-position, though it seem to be ignored by browser anyway)
  3. Use svg2ttf NPM package to generate TTF file
  4. Use FontForge to generate WOFF and WOFF2 files from TTF file

Problem is this process is too complex, it would be nice to have one tool to generate fonts from SVG.

jossef commented 5 years ago

thanks for the info. doing some reverse engineering to get a clue of what tool was used to generate these fonts.

fc-scan MaterialIcons-Regular.ttf 

Pattern has 24 elts (size 32)
    family: "Material Icons"(s)
    familylang: "en"(s)
    style: "Regular"(s)
    stylelang: "en"(s)
    fullname: "Material Icons"(s)
    fullnamelang: "en"(s)
    slant: 0(i)(s)
    weight: 80(i)(s)
    width: 100(i)(s)
    spacing: 100(i)(s)
    foundry: "unknown"(s)
    file: "MaterialIcons-Regular.ttf"(s)
    index: 0(i)(s)
    outline: True(s)
    scalable: True(s)
    charset: 
    0000: 00000000 03ff0000 80000000 07fffffe 00000000 00000000 00000000 00000000
    00e0: fa00001f fbfbdf1b ffefffff 001fffff 00000000 dfff8000 ffffffd8 0001ffff
    00e1: 00000000 00000000 ffffffe0 03ffffff 103f0000 fffffff8 0f01e10f c0000007
    00e2: 00000007 ffffffc0 ffffffff 00001fff 00000000 f0000000 000013df 00000000
    00e3: fdfffd80 07ffffff 00000000 00000000 e0000000 ffffffff ffffffff fffe7ffd
    00e4: ffefffff ffff7fff 00000001 00000000 00000000 00000000 00000000 00000000
    00e5: 00000000 fe7fe000 ffffffff 3fffffff 00000000 00000000 fffffff8 00000003
    00e6: ffffc000 fffffff9 000003ff 00000000 00000000 00000000 a0000030 00000002
    00e7: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ffffc200
    00e8: 00fe7803 07f00000 fffbe000 ffefffff ffffffff ff7fffff ffff7fff ffffffff
    00e9: fffeff7d ffffffff ffffffaf bfffffff 2d548b04 3cd7252e 23deff28 526cb253
    00ea: fffffe07 03ffffdf 03ff03ff 001f03ff 00000000 00000000 00000000 00000000
    00eb: 00000000 f8000000 0000dfff 00000000 00000000 00000000 00000000 00000000
    10ff: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 20000000
(s)
    lang: (s)
    fontversion: 66650(i)(s)
    capability: "otlayout:latn"(s)
    fontformat: "TrueType"(s)
    decorative: False(s)
    postscriptname: "MaterialIcons-Regular"(s)
    color: False(s)
    symbol: False(s)

guessing google uses https://github.com/google/woff2 to generate woff2

jossef commented 5 years ago

seems they use fontforge to generate the fonts. some traces from the compiled font info

image

my guess - they have a template icon and in their build process they copy it and populate with icons. then export a font files (ttf + woff)

they goat icon probably is the only thing in the template, preserving some settings

image

cyberalien commented 5 years ago

Possibly, but that tool only compresses/decompresses fonts, font has to be created first.

I did some more tests on WOFF and WOFF2 files. FontForge generates mess. WOFF2 files are actually bigger than WOFF, so there is definitely something fishy going on.

Files generated by FontForge:

Then I found these packages: https://github.com/fontello/svg2ttf https://github.com/fontello/ttf2woff and https://github.com/nfroidure/ttf2woff2

Using those packages to convert SVG font (generated by FontForge + custom fixes) to other formats, sizes make more sense:

So I think way to go is this:

cyberalien commented 5 years ago

One more thing that I've discovered when testing fonts: fontkit incorrectly exports glyphs, so for now it cannot be used to export glyphs from font.

This is code fontkit exports for account_circle:

M184.5 122Q184.5 122 203.25 112Q222 102 256 102Q290 102 327.5 122Q365 142 384 171Q383 199 339 218Q295 237 256 237Q217 237 173 218.5Q129 200 128 171Q147 142 184.5 122ZM301 386Q301 386 291.5 395.5Q282 405 256 405Q230 405 211 386Q192 367 192 341Q192 315 211 296Q230 277 256 277Q282 277 301 296Q320 315 320 341Q320 367 301 386ZM105.5 406.5Q105.5 406.5 136.75 437.75Q168 469 256 469Q344 469 406.5 406.5Q469 344 469 256Q469 168 406.5 105.5Q344 43 256 43Q168 43 105.5 105.5Q43 168 43 256Q43 344 105.5 406.5Z

and this is what FontForge generates when font is converted to SVG format:

M256 102q34 0 71.5 20t56.5 49q-1 28 -45 47t-83 19t-83 -18.5t-45 -47.5q19 -29 56.5 -49t71.5 -20zM256 405q-26 0 -45 -19t-19 -45t19 -45t45 -19t45 19t19 45t-19 45t-45 19zM256 469q88 0 150.5 -62.5t62.5 -150.5t-62.5 -150.5t-150.5 -62.5t-150.5 62.5
t-62.5 150.5t62.5 150.5t150.5 62.5z

by wrapping path in SVG stuff to make icon, result from FontKit is this:

<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><g transform="translate(0, 512) scale(1, -1)"><path d="M184.5 122Q184.5 122 203.25 112Q222 102 256 102Q290 102 327.5 122Q365 142 384 171Q383 199 339 218Q295 237 256 237Q217 237 173 218.5Q129 200 128 171Q147 142 184.5 122ZM301 386Q301 386 291.5 395.5Q282 405 256 405Q230 405 211 386Q192 367 192 341Q192 315 211 296Q230 277 256 277Q282 277 301 296Q320 315 320 341Q320 367 301 386ZM105.5 406.5Q105.5 406.5 136.75 437.75Q168 469 256 469Q344 469 406.5 406.5Q469 344 469 256Q469 168 406.5 105.5Q344 43 256 43Q168 43 105.5 105.5Q43 168 43 256Q43 344 105.5 406.5Z" /></g></svg>

This is what it looks like (I've added green circle so you could see problem): Screen Shot 2019-06-08 at 10 37 49

and this is what same icon looks like when using path from FontForge: Screen Shot 2019-06-08 at 10 52 22

I'll make reduced test case for fontkit when I'll have time, so they could fix bug in their script.

jossef commented 5 years ago

I think we can do it 100% fontforge. working on a POC to generate an .sfd fontforge file (textual format) -> and then generate ttf font

for instance the representation of network_wifi character in the .sfd file

StartChar: uniE1BA
Encoding: 57786 57786 272
Width: 512
GlyphClass: 3
Flags: W
LayerCount: 2
Fore
SplineSet
436 279 m 1,0,-1
 437 278 l 1,1,-1
 256 54 l 1,2,-1
 75 278 l 1,3,4
 75 278 75 278 76 279 c 2,5,-1
 8 363 l 1,6,7
 129 448 129 448 256 448 c 128,-1,8
 383 448 383 448 504 363 c 1,9,-1
 436 279 l 1,0,-1
EndSplineSet
Ligature2: "'rlig' Required Ligatures in Latin lookup 0 subtable" n e t w o r k underscore w i f i
EndChar
jossef commented 5 years ago

struggling to understand how to properly convert an svg file (specifically the shape) to SplineSet

this is baseline-wallpaper-24px.svg from material.io

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
    <path d="M4 4h7V2H4c-1.1 0-2 .9-2 2v7h2V4zm6 9l-4 5h12l-3-4-2.03 2.71L10 13zm7-4.5c0-.83-.67-1.5-1.5-1.5S14 7.67 14 8.5s.67 1.5 1.5 1.5S17 9.33 17 8.5zM20 2h-7v2h7v7h2V4c0-1.1-.9-2-2-2zm0 18h-7v2h7c1.1 0 2-.9 2-2v-7h-2v7zM4 13H2v7c0 1.1.9 2 2 2h7v-2H4v-7z"/>
    <path d="M0 0h24v24H0z" fill="none"/>
</svg>

this is the matching part in the .std file

SplineSet
85 235 m 1,0,-1
 85 85 l 1,1,-1
 235 85 l 1,2,-1
 235 43 l 1,3,-1
 85 43 l 2,4,5
 68 43 68 43 55.5 55.5 c 128,-1,6
 43 68 43 68 43 85 c 2,7,-1
 43 235 l 1,8,-1
 85 235 l 1,0,-1
427 85 m 1,9,-1
 427 235 l 1,10,-1
 469 235 l 1,11,-1
 469 85 l 2,12,13
 469 68 469 68 456.5 55.5 c 128,-1,14
 444 43 444 43 427 43 c 2,15,-1
 277 43 l 1,16,-1
 277 85 l 1,17,-1
 427 85 l 1,9,-1
427 469 m 2,18,19
 444 469 444 469 456.5 456.5 c 128,-1,20
 469 444 469 444 469 427 c 2,21,-1
 469 277 l 1,22,-1
 427 277 l 1,23,-1
 427 427 l 1,24,-1
 277 427 l 1,25,-1
 277 469 l 1,26,-1
 427 469 l 2,18,19
363 331 m 128,-1,28
 363 318 363 318 353.5 308.5 c 128,-1,29
 344 299 344 299 331 299 c 128,-1,30
 318 299 318 299 308.5 308.5 c 128,-1,31
 299 318 299 318 299 331 c 128,-1,32
 299 344 299 344 308.5 353.5 c 128,-1,33
 318 363 318 363 331 363 c 128,-1,34
 344 363 344 363 353.5 353.5 c 128,-1,27
 363 344 363 344 363 331 c 128,-1,28
213 235 m 1,35,-1
 277 156 l 1,36,-1
 320 213 l 1,37,-1
 384 128 l 1,38,-1
 128 128 l 1,39,-1
 213 235 l 1,35,-1
85 427 m 1,40,-1
 85 277 l 1,41,-1
 43 277 l 1,42,-1
 43 427 l 2,43,44
 43 444 43 444 55.5 456.5 c 128,-1,45
 68 469 68 469 85 469 c 2,46,-1
 235 469 l 1,47,-1
 235 427 l 1,48,-1
 85 427 l 1,40,-1
EndSplineSet

haven't found a good way to convert a shape/svg to the fontforge SplineSet so went on the naive approach of creating a font with one glyph and exporting it

def main():
    font = fontforge.font()
    unicode_id = 57344
    unicode_name = 'uni' + hex(unicode_id).upper()[2:]
    char = font.createChar(unicode_id, unicode_name)
    glyph = char.importOutlines('file.svg')
    font.save('result.sfd')

the result is different :/

SplineSet
166.666992188 633.333007812 m 1
 166.666992188 341.666992188 l 1
 83.3330078125 341.666992188 l 1
 83.3330078125 633.333007812 l 2
 83.3330078125 679.166992188 120.833007812 716.666992188 166.666992188 716.666992188 c 2
 458.333007812 716.666992188 l 1
 458.333007812 633.333007812 l 1
 166.666992188 633.333007812 l 1
416.666992188 258.333007812 m 1
 540.416992188 103.75 l 1
 625 216.666992188 l 1
 750 50 l 1
 250 50 l 1
 416.666992188 258.333007812 l 1
708.333007812 445.833007812 m 0
 708.333007812 411.25 680.416992188 383.333007812 645.833007812 383.333007812 c 0
 611.25 383.333007812 583.333007812 411.25 583.333007812 445.833007812 c 0
 583.333007812 480.416992188 611.25 508.333007812 645.833007812 508.333007812 c 0
 680.416992188 508.333007812 708.333007812 480.416992188 708.333007812 445.833007812 c 0
833.333007812 716.666992188 m 2
 879.166992188 716.666992188 916.666992188 679.166992188 916.666992188 633.333007812 c 2
 916.666992188 341.666992188 l 1
 833.333007812 341.666992188 l 1
 833.333007812 633.333007812 l 1
 541.666992188 633.333007812 l 1
 541.666992188 716.666992188 l 1
 833.333007812 716.666992188 l 2
833.333007812 -33.3330078125 m 1
 833.333007812 258.333007812 l 1
 916.666992188 258.333007812 l 1
 916.666992188 -33.3330078125 l 2
 916.666992188 -79.1669921875 879.166992188 -116.666992188 833.333007812 -116.666992188 c 2
 541.666992188 -116.666992188 l 1
 541.666992188 -33.3330078125 l 1
 833.333007812 -33.3330078125 l 1
166.666992188 258.333007812 m 1
 166.666992188 -33.3330078125 l 1
 458.333007812 -33.3330078125 l 1
 458.333007812 -116.666992188 l 1
 166.666992188 -116.666992188 l 2
 120.833007812 -116.666992188 83.3330078125 -79.1669921875 83.3330078125 -33.3330078125 c 2
 83.3330078125 258.333007812 l 1
 166.666992188 258.333007812 l 1
0 800 m 1
 1000 800 l 1
 1000 -200 l 1
 0 -200 l 1
 0 800 l 1
EndSplineSet
cyberalien commented 5 years ago

I think using FontForge's importOutlines is best way to go. Shapes are not always simple, there are masks in some icons, multiple paths. FontForge seem to be able to handle them very well.

jossef commented 5 years ago

asked here https://stackoverflow.com/q/56506104/3191896

cyberalien commented 5 years ago

Just letting you know that I haven't abandoned this, just didn't have any time to work on it yet.

Also I'm working on icon finder script that can be used to list and search icons that will be very useful for this set.

jossef commented 5 years ago

👍 same, working on this mainly on weekends (lately low on spare time)

cyberalien commented 5 years ago

There are some good news. Google has published new meta data for icons in easy to use format. URL: https://fonts.google.com/metadata/icons

It is up to date with font, so instead of parsing font you can just grab meta data to get list of available icons.

They have also fixed all broken icons. No more black squares.

cyberalien commented 4 years ago

I've made a script for pulling latest icons and building font. Here is result (font, css, sample file):

Archive.zip

For import I've used this method:

Take a look at sample. Most icons are working correctly. However many have weird circles. So there are issues. But at least process is working and every step is separate so its easy to adjust.

cyberalien commented 4 years ago

Done! Fixed everything. Broken icons were because of bug in FontForge. It doesn't support compressed arcs in shapes. Also found few other bugs.

Archive.zip

I'll clean up and publish everything this week. Current plan is to make several repositories:

For svg repository I'll just rename my old repository. No point in duplicating.

cyberalien commented 4 years ago

Published build scripts, font, moved my old SVG and PNG repositories to this organisation.

Later will update readme files and create pages for list of icons.

SVG: https://github.com/material-icons/material-icons PNG: https://github.com/material-icons/material-icons-png Font: https://github.com/material-icons/material-icons-font

Closing this issue.

jossef commented 4 years ago

nice! good job mate! have you added support in ligatures?

cyberalien commented 4 years ago

No, unfortunately its not possible for two tone icons.

cyberalien commented 4 years ago

Updated everything.

Check out readme for syntax: https://github.com/material-icons/material-icons-font What do you think about that syntax?

Also test page with all icons: https://material-icons.github.io/material-icons-font/samples/test.html

jossef commented 4 years ago

Updated everything.

Check out readme for syntax: https://github.com/material-icons/material-icons-font What do you think about that syntax?

Also test page with all icons: https://material-icons.github.io/material-icons-font/samples/test.html

bravo man. impressive.

regarding syntax

I can offer help making a demo page

cyberalien commented 4 years ago

Codepoints are stored in data.json in font repository, so builds always return same code points.

Class names: that makes sense. I do like that. It would be easy to transition to such class names from ligatures. For font variations then maybe additional classes like material-icons-outline instead of current md-?

Demo page: thanks, that would be nice.

cyberalien commented 4 years ago

Class names won't work for some icons such as "3d_rotation" because class name cannot start with number. So icons must have some prefix.

cyberalien commented 4 years ago

Created new build. Icon class names are changed to use underscore, like in old ligatures, but with md- prefix:

<i class="material-icons md-signal_wifi_4_bar"></I>

Also changed class names for font variations a bit. Instead of adding extra class, main class can be changed to use font variations, like material-icons-twotone for two tone icon:

<i class="material-icons-twotone md-videocam_off"></i>
cyberalien commented 4 years ago

About directory structure of fonts repository. Do you think it would be a good idea to change it to match your font's repository, generate files like _variables.scss that exist in your repo?

That way if you don't mind, your font repository can be moved here and new font added as new version. You currently have version 5.0.1 in package.json, this could be version 6.0.0 with slightly changed syntax, but same file names.

It could give project more exposure and people might start requesting custom icons here instead of opening issues at google's unmaintained repository.

jossef commented 4 years ago

Class names won't work for some icons such as "3d_rotation" because class name cannot start with number. So icons must have some prefix.

see $class-name: normalize-class-name($class-name); https://github.com/jossef/material-design-icons-iconfont/blob/master/src/_mixins.scss

cyberalien commented 4 years ago

I see two issues with that idea:

  1. Inconsistent class names.

  2. Conflicting class names. As ligatures there are no issues, but when ligatures become class names, they might conflict with another class name from user's stylesheet. For example "slideshow", "group", "texture", "hd", "filter", "feedback", "error" are generic words that could be used for other purposes, so if someone has .error in their stylesheet to display error block, its rules would bleed into icon.

jossef commented 4 years ago

good point, let's chat this weekend (hangouts)

cyberalien commented 4 years ago

Can't. Tomorrow I'm leaving for almost a week. Maybe weekend after that?

jossef commented 4 years ago

sure thing.

On Thu, Oct 17, 2019 at 2:26 PM Vjacheslav Trushkin < notifications@github.com> wrote:

Can't. Tomorrow I'm leaving for almost a week. Maybe weekend after that?

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/material-icons/build/issues/1?email_source=notifications&email_token=AAJ2HOXGIALBGFZMY7CE7UTQPBDVTA5CNFSM4HPVKAY2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBPYDDI#issuecomment-543129997, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJ2HOVIG3PXZWCBMWHUL5LQPBDVTANCNFSM4HPVKAYQ .

jimmykane commented 4 years ago

:-D Any update ?

cyberalien commented 4 years ago

:-D Any update ?

Forgot about that and got distracted with other work.

Everything in this repository should be working, including font. It doesn't use ligatures because as far as I know, it isn't possible to implement ligatures for two tone icons without some browser specific shenanigans, so it is similar to other icon fonts and works in older browsers. That is it uses classes and placeholders. See https://github.com/material-icons/material-icons-font

I'll run update script to make sure all latest icons will be included.