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