Google fonts Json format

Hi !
Did you already let your users chose a font from google fonts ?
I found a cool Json file that is listing all google fonts https://raw.githubusercontent.com/jonathantneal/google-fonts-complete/master/google-fonts.json and I would like to display it in a select field.

Of course, I have to re-write the Json because

  category:
    label: Category
    type: select 
    options: https://raw.githubusercontent.com/jonathantneal/google-fonts-complete/master/google-fonts.json

doesn’t work…
How could I change the structure of this Json file to be compatible with Kirby ?

The idea is to have something like

{
  "font1-regular": "font1-regular",
  "font1-italic": "font1-italic",
  "font2-regular": "font2-regular",
  "font3-regular": "font3-regular"
  ...
}

You have to flatten the array, either with some foreach loops or with some array voodoo, for example:

<?php
$file = $kirby->roots()->assets().'/fonts.json';
$json = f::read($file);
$array = json_decode($json, true);

$fonts = [];

foreach($array as $key => $item) {
  foreach($item['variants'] as $k => $var) {
    $fonts[$key.'-'.$k] = $key.'-'.$k;
  }

}
echo json_encode($fonts)

However, the JSON file you mentioned contains some deeper nested variants, so if you want to include these as well, you have to go further down.

Okay, but I don’t know if I have to modify the select field from the core, or create a plugin file? I just understood the url path must be set like this

options: url
url: api/google-fonts.json

(and it’s working) but I don’t know how to link your script to my JSON file

You could put the above code into a route, in my example above, I saved the original Json as a file, but you might as well read it from the url:

$json = f::read('https://raw.githubusercontent.com/jonathantneal/google-fonts-complete/master/google-fonts.json');
c::set('routes', [
  [
    'pattern' => 'api/fonts',
    'action'  => function() {
      $json = f::read('https://raw.githubusercontent.com/jonathantneal/google-fonts-complete/master/google-fonts.json');
      $array = json_decode($json, true);

      $fonts = [];

      foreach($array as $key => $item) {
        foreach($item['variants'] as $k => $var) {
          $fonts[$key.'-'.$k] = $key.'-'.$k;
        }

      }
      return response::json($fonts);
    }
  ]
]);

Don’t know if this is wise, reading that file like this.

Blueprint:

  font:
    label: Select font
    type: select
    options: url
    url: api/fonts

You might as well query the Google Fonts API directly.

Thank you ! The select field is working fine, I can select all the fonts. I could query their API but I don’t want to ask them an another API-Key again, and this file is regularly updated :wink:

By the way, the last step demands some PHP skills, and I don’t have so much : the echo from the select field is returning the font name like Abel-normal and I need to embed it without the “normal/bold/italic”

like

<link href="https://fonts.googleapis.com/css?family=Abel" rel="stylesheet">

font-family: 'Abel', sans-serif;

And with Open Sans

<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
font-family: 'Open Sans', sans-serif;

You should probably have a way to set the weights. That will just give you 400 weight (aka normal)

Trouble is, the weights vary, so you would need fill a second multi select field up with the weights for the font.

For example, Roboto is epic and has up to 12 weights:

<link href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i" rel="stylesheet">

That’s what I already said above, if you want to get the weights and additional information, you have to go further down into the array. But a second select field doesn’t make that much sense as the number of available weights varies from one font to the other, if you really want to include weights, you would have to flatten the array further.

Or, you would have to query the API for each font to get the correct weights to choose from, with a conditional select.

@Oziris: hm, you wanted the variants information in your select values. Two options: change the generated array to not include the weight. Get the font only with some string extraction from the value.

$font = 'Abel-normal';
$substring = substr($font, 0, strpos($font, '-'));
echo $substring;

Ok, but there is so much fonts that just having “regular” does not bother me too much ! :slight_smile: and not having enough PHP skills, I would not ask you too much. Maybe an idea of interesting plugins I think!

Anyway, do you know just a simple way to remove “normal / bold or italic” and set “fontname + subname” to the variable?

See my reply above….

I think the way i would tackle this is that not every font on google fonts is going to suit the design of the site. Why don’t you just pick 3 of 4 font pairings and weights and allow choices between those, from a select? That way you could just hard wire the appropriate links to the fonts in a snippet or something.

Wonderful ! Many thanks !

It depends of the template, and I think letting the user choose it could be a great stuff. And I already know how to switch between multiple entries already, so I can learn something on top of that !

Anyway, if you want an even longer list, here you go with weights:

foreach($array as $key => $item) {
  foreach($item['variants'] as $k => $var) {
    foreach($var as $weight => $value) {
      $fonts[$key.'-'.$k . '-' . $weight] = $key.'-'.$k. '-' . $weight;
    }

  }
}
dump(json_encode($fonts));
//only 2201 entries to choose from now;)

Oh sure, i don’t mean to rain on your parade, but speaking as a professional designer, I would really hate it if someone picked comic sans :slight_smile:

1 Like

Apart from choosing an appropriate font for a design, there’s also the problem that a long list of options to choose from is not really very user friendly. Without the weights, you already have a list of almost 900 options…

How about a config variable with a string of specific fonts that are sympathetic to the design, that you could feed into the code from @texnixe so you didn’t end up with 2201 entries to chose from?

Or maybe it could be really usefull for the programmer to swith quickly bewteen fonts to adapt its design instead of copy-paste the code generated by google ?

As above :slight_smile:

If you just want it for testing purposes, to try out different fonts, this might make sense. For a normal user I think this number of choices is overkill.

1 Like

I think both are awesome ! :slight_smile:
Glad to have passionate crowds on this thread

However, it is not difficult to filter the list by categories like sans-serif etc. to at least limit it some more and require that it has both normal & italic variants etc. Here an example with some conditions applied:

$file = $kirby->roots()->assets().'/fonts.json';
$json = f::read('https://raw.githubusercontent.com/jonathantneal/google-fonts-complete/master/google-fonts.json');

$fontArray = json_decode($json, true);

$fonts = [];

foreach($fontArray as $font => $details) {
    if($details['category'] === 'sans-serif' && isset($details['variants']['normal']['200']) && isset($details['variants']['italic'])):
      foreach($details['variants'] as $variant => $var) {
          $fonts[$font.'-'.$variant] = $font.'-'.$variant ;
      }
    endif;
}
dump($fonts);
echo count($fonts);

Or here using array_filter with even more specific requirements:

$json = f::read('https://raw.githubusercontent.com/jonathantneal/google-fonts-complete/master/google-fonts.json');

$fontArray = json_decode($json, true);

$callback = function($details, $font) {
  return
  // we only want sans-serif fonts
  $details['category'] === 'sans-serif' &&
  // and they should provide the greek and latin subsets
  !array_diff(['greek', 'latin'], $details['subsets']) &&
  // we want to have fonts that provide both normal and italic variants
  !array_diff(['normal', 'italic'],array_keys($details['variants'])) &&
  // and the normal variants should have three weights
  !array_diff(['400', '500', '700'],array_keys($details['variants']['normal']));
};
$result = array_filter($fontArray, $callback, ARRAY_FILTER_USE_BOTH);
$fonts = [];
foreach($result as $font => $details) {
  foreach($details['variants'] as $variant => $var) {
    $fonts[$font.'-'.$variant] = $font.'-'.$variant ;
  }
}

dump($fonts);
echo count($fonts); // 14
1 Like