graphicore / specimenTools

Apache License 2.0
29 stars 5 forks source link

Incorrect handling of font widths (and styles?) in FontsData #15

Open kontur opened 7 years ago

kontur commented 7 years ago

So this issue was brought to my attention by one of the users of my Wordpress plugin using this library. Using two fonts of a family of the same weight and style but with differing widths will fail to display any but the first font. For example a narrow and extended cut.

The warning in FontsData will get triggered, but obviously it is expected that both files have same weight and style, so this is a false positive and misleading. Both fonts are still loaded and are present in the ._data object. In the same function, the returned result gets reduced to single entries for each weight (at least that's what I think happens in this slightly dense code at the function's end), so widgets like the FamilyChooser will only receive one font.

(Related, but of less importance, the FamilyChooser's buttons fail to show the width, so will display a narrow bold as "Familyname 700" - so even if it correctly received both fonts, they both would show as "Familyname 700" for a bold narrow and bold extended.)

I also saw how the family name is extracted in the same file and was wondering if essentially the problem here is a semantic one. For example, is Open Sans a family, or are Open Sans Regular, Open Sans Condensed and Open Sans Extended separate families? I understand the naming scheme to work like FamilyName-WidthWeightStyle, so that would put width next to weight and style, only the library doesn't handle it like that.

Upon inspecting some more how the returned value is structured I noticed that for a Regular and Italic of same weight and family there also is only one value returned, Family > Weight > FontId - so am I missing the point or are both issues the same and need a solution for better structuring the returned family values that exposes style, weight and width?

kontur commented 7 years ago

On a side note, this also relates to #4 and possibly using the font-stretch css attribute to define and use those widths in the css.

graphicore commented 7 years ago

For example, is Open Sans a family, or are Open Sans Regular, Open Sans Condensed and Open Sans Extended separate families?

How it is done now is influenced by google fonts. There, different font widths are indeed handled as different families:

https://fonts.google.com/specimen/Open+Sans https://fonts.google.com/specimen/Open+Sans+Condensed

Though, these fonts are outdated and don't use the current file naming scheme. Look at https://github.com/google/fonts/tree/master/ofl/asapcondensed or https://github.com/google/fonts/tree/master/ofl/cabincondensed for current examples.

It's a very fragile business to deal with these naming (and other) conventions and it's obvious that everyone can have their own scheme. Maybe, we should make it easy to extend or configure the getFamiliesData function?

Also note that we use other google fonts conventions, like in weight2weightName and weight2cssWeight.


the returned result gets reduced to single entries for each weight (at least that's what I think happens in this slightly dense code at the function's end),

No, see, theres a continue directly after the warning. The fontIndex is never stored in styleDict[fontStyle] = fontIndex;.

this slightly dense code at the function's end)


// families is an Object with key value pairs, like this:
/*
{
myTopFamily: {200: {regular: 1, italic: 2}, 400: {regular: 5, italic: 8}}
, ASuperFamily: {200: {regular: 4, italic: 3}}
}
*/

result = Object.keys(families).sort() // now we have an alphabetically sorted list: ['ASuperFamily', 'myTopFamily'] .map(function(key){ return [key, this[key]];}, families); // map returns an array of [key, families[key]] items, thus: / [ ['ASuperFamily', {200: {regular: 4, italic: 3}}] , ['myTopFamily', {200: {regular: 1, italic: 2}, 400: {regular: 5, italic: 8}}] ] /


This is very explicitly defining the family order. Although, JavaScript objects usually do keep insertion order, they are not required to do so. Arrays are the definite way to keep an order.
kontur commented 7 years ago

I agree that getFamiliesData could be modified in some way to allow retrieving the fonts without reduction to single weight & style instances.

What I meant with the entries remaining is rather that the loaded fonts are still accessible via the window wide font index.

I attempted a preliminary addition of widths that defines the css and FontsData caches correctly. In a quite rudimentary way this just grabs the width from the family name, since I don't know if there is any other table entries or the like where we could scoop this info up from. I've left the getFamiliesData untouched, since for my listing of the different widths via my FontLister implementation this is suffice, but I imagine we could just pass in a flag to the function that would tell it what kind of reduction & sorting there should be. As default I'd suggest returning all loaded files in definition order.