Handle "strange" font weights

Fontconfig's font weights are almost but not entirely defined by the
constants found in its source code. Things like FC_WEIGHT_LIGHT are not,
as we had presumed, a list of definitive weights. As such, it seems there
are a goodly number of fonts that have weights not appearing in the list.

For example, say there's a font that is heavier than Medium (weight 100 for
fontconfig, and 6 for us) but not as heavy as Demibold (FC: 180, GS: 7),
then it might tell Fontconfig it has a weight of 130.

When this happens, we _could_ assign its NSWeight to be 6 or 7, but it's
possible that there will be another face in that font that ALSO fits there,
which might throw off the sorting. Instead, what I suggest is to do what
I have done...assign an NSWeight with a weight class that represents the
numeric distance _between_ the two defined values.

So the GS weight for this font face becomes something like:

NSWeight = 6 + ((FCWeight - Medium) * (1.0 / (Demibold - Medium)))

or, in numeric terms, 6 + 0.375

In service of this change, I have switched the actual Weight value within
the font dictionary from int to float. This is an attempt to translate this
situation into a sortable form we can work with, compressing an approximate
range of possible FC font weights into our 0-15 system.

There may well be a better way to go about this, such as figuring out where
fontconfig gets this info in the first place and converting from the source
directly, but accessing the SFNT tables (which will almost certainly be
required) is something for a later day.
This commit is contained in:
Jeff Teunissen 2019-03-24 23:55:12 -04:00
parent 5737e249f5
commit d0a9f5b943

View file

@ -53,6 +53,29 @@
#define FC_WEIGHT_ULTRABLACK FC_WEIGHT_BLACK
#endif
static float
convertWeight (int fcWeight, int bottomValue, int topValue)
{
/*
This is the distance between topValue and bottomValue expressed as a
fraction between zero and one. We do this to express the range of
fontconfig font weights in a useful manner.
*/
if (fcWeight <= bottomValue)
{
return 0.0;
}
else if (fcWeight >= topValue)
{
return 1.0;
}
else
{
return (float) (fcWeight - bottomValue) * (1.0f / (topValue - bottomValue));
}
}
static NSComparisonResult
sortFontFacesArray(id fontArr1, id fontArr2, void *context)
{
@ -65,8 +88,8 @@ sortFontFacesArray(id fontArr1, id fontArr2, void *context)
*/
NSString *style1 = [fontArr1 objectAtIndex: 1];
NSString *style2 = [fontArr2 objectAtIndex: 1];
int weight1 = [[fontArr1 objectAtIndex: 2] intValue];
int weight2 = [[fontArr2 objectAtIndex: 2] intValue];
float weight1 = [[fontArr1 objectAtIndex: 2] floatValue];
float weight2 = [[fontArr2 objectAtIndex: 2] floatValue];
unsigned int traits1 = [[fontArr1 objectAtIndex: 3] unsignedIntValue];
unsigned int traits2 = [[fontArr2 objectAtIndex: 3] unsignedIntValue];
@ -123,7 +146,8 @@ NSMutableDictionary * __allFonts;
// Make a GNUstep style font descriptor from a FcPattern
static NSArray *faFromFc(FcPattern *pat)
{
int weight, slant, spacing, width, nsweight;
int weight, slant, spacing, width;
float nsweight;
unsigned int nstraits = 0;
char *fcfamily, *fcstyle;
NSMutableString *name, *family, *style;
@ -150,99 +174,107 @@ static NSArray *faFromFc(FcPattern *pat)
family = [NSMutableString stringWithUTF8String: fcfamily];
style = [NSMutableString stringWithCapacity: 100];
switch (weight)
if (weight < FC_WEIGHT_ULTRALIGHT)
{
case FC_WEIGHT_THIN:
[style appendString: @"Thin"];
nsweight = 1;
break;
case FC_WEIGHT_ULTRALIGHT:
[style appendString: @"Ultralight"];
nsweight = 2;
break;
case FC_WEIGHT_LIGHT:
[style appendString: @"Light"];
nsweight = 3;
break;
case FC_WEIGHT_BOOK:
[style appendString: @"Book"];
nsweight = 4;
break;
case FC_WEIGHT_REGULAR:
// [style appendString: @"Regular"];
nsweight = 5;
break;
case FC_WEIGHT_MEDIUM:
[style appendString: @"Medium"];
nsweight = 6;
break;
case FC_WEIGHT_DEMIBOLD:
[style appendString: @"Demibold"];
nsweight = 7;
break;
case FC_WEIGHT_BOLD:
[style appendString: @"Bold"];
nsweight = 9;
nstraits |= NSBoldFontMask;
break;
case FC_WEIGHT_ULTRABOLD:
[style appendString: @"Ultrabold"];
nsweight = 11;
nstraits |= NSBoldFontMask;
break;
case FC_WEIGHT_BLACK:
[style appendString: @"Black"];
nsweight = 12;
nstraits |= NSBoldFontMask;
break;
case FC_WEIGHT_ULTRABLACK:
[style appendString: @"Ultrablack"];
nsweight = 13;
nstraits |= NSBoldFontMask;
break;
default:
nsweight = 5;
[style appendString: @"Thin"];
nsweight = 1 + convertWeight (weight, FC_WEIGHT_THIN, FC_WEIGHT_ULTRALIGHT);
}
else if (weight < FC_WEIGHT_LIGHT)
{
[style appendString: @"Ultralight"];
nsweight = 2 + convertWeight (weight, FC_WEIGHT_ULTRALIGHT, FC_WEIGHT_LIGHT);
}
else if (weight < FC_WEIGHT_BOOK)
{
[style appendString: @"Light"];
nsweight = 3 + convertWeight (weight, FC_WEIGHT_LIGHT, FC_WEIGHT_BOOK);
}
else if (weight < FC_WEIGHT_REGULAR)
{
[style appendString: @"Book"];
nsweight = 4 + convertWeight (weight, FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR);
}
else if (weight < FC_WEIGHT_MEDIUM)
{
nsweight = 5 + convertWeight (weight, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM);
}
else if (weight < FC_WEIGHT_DEMIBOLD)
{
[style appendString: @"Medium"];
nsweight = 6 + convertWeight (weight, FC_WEIGHT_MEDIUM, FC_WEIGHT_DEMIBOLD);
}
else if (weight < FC_WEIGHT_BOLD)
{
[style appendString: @"Demibold"];
nsweight = 7 + convertWeight (weight, FC_WEIGHT_DEMIBOLD, FC_WEIGHT_BOLD);
}
else if (weight < FC_WEIGHT_ULTRABOLD)
{
[style appendString: @"Bold"];
nsweight = 9 + convertWeight (weight, FC_WEIGHT_BOLD, FC_WEIGHT_ULTRABOLD);
nstraits |= NSBoldFontMask;
}
else if (weight < FC_WEIGHT_BLACK)
{
[style appendString: @"Ultrabold"];
nsweight = 11 + convertWeight (weight, FC_WEIGHT_ULTRABOLD, FC_WEIGHT_BLACK);
nstraits |= NSBoldFontMask;
}
else if (weight < FC_WEIGHT_ULTRABLACK)
{
[style appendString: @"Black"];
nsweight = 12 + convertWeight (weight, FC_WEIGHT_BLACK, FC_WEIGHT_ULTRABLACK);
nstraits |= NSBoldFontMask;
}
else
{
[style appendString: @"Ultrablack"];
nsweight = 13 + convertWeight (weight, FC_WEIGHT_ULTRABLACK, FC_WEIGHT_ULTRABLACK + 20);
nstraits |= NSBoldFontMask;
}
if (FcPatternGetInteger(pat, FC_WIDTH, 0, &width) == FcResultMatch)
switch (width)
{
case FC_WIDTH_ULTRACONDENSED:
[style appendString: @"Ultracondensed"];
nstraits |= NSCondensedFontMask;
break;
case FC_WIDTH_EXTRACONDENSED:
[style appendString: @"Extracondensed"];
nstraits |= NSCondensedFontMask;
break;
case FC_WIDTH_CONDENSED:
[style appendString: @"Condensed"];
nstraits |= NSCondensedFontMask;
break;
case FC_WIDTH_SEMICONDENSED:
[style appendString: @"Semicondensed"];
nstraits |= NSCondensedFontMask;
break;
case FC_WIDTH_SEMIEXPANDED:
[style appendString: @"Semiexpanded"];
nstraits |= NSExpandedFontMask;
break;
case FC_WIDTH_EXPANDED:
[style appendString: @"Expanded"];
nstraits |= NSExpandedFontMask;
break;
case FC_WIDTH_EXTRAEXPANDED:
[style appendString: @"Extraexpanded"];
nstraits |= NSExpandedFontMask;
break;
case FC_WIDTH_ULTRAEXPANDED:
[style appendString: @"Ultraexpanded"];
nstraits |= NSExpandedFontMask;
break;
default:
break;
}
{
if (width < FC_WIDTH_EXTRACONDENSED)
{
[style appendString: @"Ultracondensed"];
nstraits |= NSCondensedFontMask;
}
else if (width < FC_WIDTH_CONDENSED)
{
[style appendString: @"Extracondensed"];
nstraits |= NSCondensedFontMask;
}
else if (width < FC_WIDTH_SEMICONDENSED)
{
[style appendString: @"Condensed"];
nstraits |= NSCondensedFontMask;
}
else if (width < FC_WIDTH_SEMIEXPANDED)
{
// do nothing, this is "regular"
}
else if (width < FC_WIDTH_EXPANDED)
{
[style appendString: @"Semiexpanded"];
nstraits |= NSExpandedFontMask;
}
else if (width < FC_WIDTH_EXTRAEXPANDED)
{
[style appendString: @"Expanded"];
nstraits |= NSExpandedFontMask;
}
else if (width < FC_WIDTH_ULTRAEXPANDED)
{
[style appendString: @"Extraexpanded"];
nstraits |= NSExpandedFontMask;
}
else
{
[style appendString: @"Ultraexpanded"];
nstraits |= NSExpandedFontMask;
}
}
switch (slant)
{
@ -280,7 +312,7 @@ static NSArray *faFromFc(FcPattern *pat)
// NSLog (@"family: %@, style: %s/%@", name, fcstyle, style);
return [NSArray arrayWithObjects: name,
style,
[NSNumber numberWithInt: nsweight],
[NSNumber numberWithFloat: nsweight],
[NSNumber numberWithUnsignedInt: nstraits],
nil];
}
@ -338,7 +370,7 @@ static NSArray *faFromFc(FcPattern *pat)
[familyArray addObject: fontArray];
[fcxft_allFontNames addObject: name];
aFont = [[faceInfoClass alloc] initWithfamilyName: familyString
weight: [[fontArray objectAtIndex: 2] intValue]
weight: [[fontArray objectAtIndex: 2] floatValue]
traits: [[fontArray objectAtIndex: 3] unsignedIntValue]
pattern: fs->fonts[i]];
[fcxft_allFonts setObject: aFont forKey: name];