So, I edited the test to make it spill the entire contents of the TEXTMETRICA instance and ran it on Windows. It did not complain of a division by zero, but printed this out:
``` status.c:274: dpi=96 (min height: 24 or 20) SM_CYSIZE: 22 (...) Emmentaler-26 6 6 3 3 4 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 7 6 3 3 4 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 8 9 4 5 6 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 9 9 4 5 6 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 10 9 4 5 6 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 11 12 6 6 8 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 12 12 6 6 8 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 13 12 6 6 8 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 15 15 7 8 10 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 16 15 7 8 10 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 20 21 10 11 14 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 22 21 10 11 14 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 28 29 13 16 19 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 36 35 16 19 23 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 48 47 22 25 31 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 72 71 33 38 47 0 9 41 500 0 1001 1001 32 255 32 32 0 0 0 59 0 Emmentaler-Brace 6 0 0 0 0 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 7 0 0 0 0 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 8 0 0 0 0 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 9 0 0 0 0 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 10 19 9 10 18 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 11 19 9 10 18 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 12 19 9 10 18 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 13 19 9 10 18 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 15 19 9 10 18 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 16 19 9 10 18 0 0 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 20 19 9 10 18 0 16 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 22 19 9 10 18 0 16 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 28 19 9 10 18 0 16 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 36 39 20 19 37 0 16 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 48 39 20 19 37 0 16 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 72 77 38 39 73 0 16 0 400 0 1001 1001 32 255 32 32 0 0 0 59 0 (...) ```
(I'm putting Emmentaler-26 along it as a bit of a control)
The first column is the font size (from the `sizes` array in the test), and the others follow this order: ``` tmHeight, tmAscent, tmDescent, tmInternalLeading, tmExternalLeading, tmAveCharWidth, tmMaxCharWidth, tmWeight, tmOverhang, tmDigitizedAspectX, tmDigitizedAspectY, tmFirstChar, tmLastChar, tmDefaultChar, tmBreakChar, tmItalic, tmUnderlined, tmStruckOut, tmPitchAndFamily, tmCharSet ```
For comparison, I added the fix in the merge request and ran it on Linux via Wine, and got this:
``` status.c:275: dpi=96 (min height: 20 or 20) SM_CYSIZE: 18 (...) Emmentaler-26 6 6 3 3 4 0 9 36 500 0 96 96 32 255 31 32 0 0 0 11 0 7 6 3 3 4 0 9 36 500 0 96 96 32 255 31 32 0 0 0 11 0 8 6 3 3 4 0 9 36 500 0 96 96 32 255 31 32 0 0 0 11 0 9 9 4 5 6 0 9 45 500 0 96 96 32 255 31 32 0 0 0 11 0 10 9 4 5 6 0 9 45 500 0 96 96 32 255 31 32 0 0 0 11 0 11 9 4 5 6 0 9 45 500 0 96 96 32 255 31 32 0 0 0 11 0 12 12 6 6 8 0 9 63 500 0 96 96 32 255 31 32 0 0 0 11 0 13 12 6 6 8 0 9 63 500 0 96 96 32 255 31 32 0 0 0 11 0 15 15 7 8 10 0 9 81 500 0 96 96 32 255 31 32 0 0 0 11 0 16 15 7 8 10 0 9 81 500 0 96 96 32 255 31 32 0 0 0 11 0 20 17 8 9 12 1 9 50 500 0 96 96 32 255 31 32 0 0 0 11 0 22 21 10 11 14 1 9 54 500 0 96 96 32 255 31 32 0 0 0 11 0 28 26 12 14 17 1 9 48 500 0 96 96 32 255 31 32 0 0 0 11 0 36 36 17 19 23 1 9 63 500 0 96 96 32 255 31 32 0 0 0 11 0 48 47 22 25 31 1 9 63 500 0 96 96 32 255 31 32 0 0 0 11 0 72 71 33 38 47 2 9 55 500 0 96 96 32 255 31 32 0 0 0 11 0 Emmentaler-Brace 6 0 0 0 0 0 16 0 400 0 96 96 255 255 31 32 0 0 0 11 0 7 0 0 0 0 0 16 0 400 0 96 96 255 255 31 32 0 0 0 11 0 8 0 0 0 0 0 16 0 400 0 96 96 255 255 31 32 0 0 0 11 0 9 0 0 0 0 0 16 0 400 0 96 96 255 255 31 32 0 0 0 11 0 10 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 11 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 12 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 13 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 15 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 16 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 20 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 22 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 28 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 36 20 10 10 18 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 48 38 19 19 37 0 16 32 400 0 96 96 255 255 31 32 0 0 0 11 0 72 58 29 29 55 0 16 16 400 0 96 96 255 255 31 32 0 0 0 11 0 (...) ```
Some things stood out:
1. The numbers in columns 2 to 4 are just slightly different. This may result from the different min height and SM_CYSIZE, but I'm not sure 2. tmAveCharWidth = 16 and tmMaxCharWidth = 16 instead of 0 in some places 3. tmDigitizedAspect[X/Y] = 96 instead of 1001 4. tmFirstChar = 255 instead of 32 5. tmDefaultChar = 31 instead of 32 6. tmPitchAndFamily = 11 instead of 59
It seems that Windows does check somewhere for tmHeight == 0 and handles it gracefully, even though that might mean the font will effectively not be printed. I looked into freetype.c and found what appears to be the source for differences 2. to 6. in freetype_set_outline_text_metrics(). I don't know much about fonts, so I'll be guessing a lot of stuff here.
For 2., it seems be the following part: ``` 3746 TM.tmAveCharWidth = SCALE_X(pOS2->xAvgCharWidth); 3747 if (TM.tmAveCharWidth == 0) { 3748 TM.tmAveCharWidth = 1; 3749 } 3750 TM.tmMaxCharWidth = SCALE_X(ft_face->bbox.xMax - ft_face->bbox.xMin); ``` Removing the check in lines 3747-3749 does make it zero (as well as tmMaxCharWidth), but then some operation down the line causes it to become equal to -2147483648 (I couldn't find where).
For 3. it's: ``` 3765 TM.tmDigitizedAspectX = 96; /* FIXME */ 3766 TM.tmDigitizedAspectY = 96; /* FIXME */ ``` So not really much I can do.
For 4., it's: ``` 3792 TM.tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */ ``` This one seems to be more correct than in Windows (although I don't know how it would affect compatibility). There is only a single character in the entire font (the left brace), and it's in the font's private use area, so it makes sense to default to 0xff as to say "there's effectively nothing here". I guess 0x20 is just the default in case pOS2->usFirstCharIndex == pOS2->usLastCharIndex.
For 5., it's: ``` 3795 if(pOS2->usFirstCharIndex <= 1) 3796 TM.tmBreakChar = pOS2->usFirstCharIndex + 2; 3797 else if (pOS2->usFirstCharIndex > 0xff) 3798 TM.tmBreakChar = 0x20; 3799 else 3800 TM.tmBreakChar = pOS2->usFirstCharIndex; 3801 TM.tmDefaultChar = TM.tmBreakChar - 1; ``` This one seems to be more consistent than in Windows. The fonts don't have complete charsets, so being off by one probably doesn't change much. I guess 0x20 is more likely to be supported in exotic fonts than 0x1f?
For 6., it's probably that: ``` WINE: tmPitchAndFamily = FF_DONTCARE | TMPF_DEVICE | TMPF_VECTOR | TMPF_FIXED_PITCH WINDOWS: tmPitchAndFamily = FF_MODERN | TMPF_DEVICE | TMPF_VECTOR | TMPF_FIXED_PITCH ``` I guess FF_MODERN is also a default?
So, it seems that checking for tmHeight == 0 might be necessary in places where a division might happen (to account for those weird fonts, at least in tests), and the calculation of tmAveCharWidth and tmMaxCharWidth *might* need some changes too. I guess we could also add an ERR call to catch fonts that might cause problems because of tmHeight == 0 or ppem == 0.