Instead of loading glyphs (with FreeType), just check loca table
Part of https://bugs.freedesktop.org/show_bug.cgi?id=64766#c47
This is the approach introduced in
https://bugs.freedesktop.org/show_bug.cgi?id=64766#c30
Testing it with 11GB worth of stuff, before/after:
behdad:src 130$ time fc-scan ~/fonts/ > before
real 2m18.428s
user 1m17.008s
sys 0m20.576s
behdad:src 0$ time fc-scan ~/fonts/ > after
real 1m12.130s
user 0m18.180s
sys 0m19.952s
Running the after case a second time is significantly faster:
behdad:src 130$ time fc-scan ~/fonts/ > after
real 0m24.825s
user 0m12.408s
sys 0m11.356s
Next I'm going to try to not even read loca...
diff --git a/src/fcfreetype.c b/src/fcfreetype.c
index 4be025a..a11e20b 100644
--- a/src/fcfreetype.c
+++ b/src/fcfreetype.c
@@ -51,7 +51,9 @@
#include <string.h>
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_ADVANCES_H
#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H
#include FT_TYPE1_TABLES_H
@@ -2197,14 +2199,25 @@
return 0;
}
+typedef struct _FcFreeTypeCheckGlyphInfo
+{
+ unsigned int num_glyphs;
+ FcBool long_offset;
+ union {
+ FcChar32 u32[1];
+ FcChar16 u16[1];
+ } offsets;
+} FcFreeTypeCheckGlyphInfo;
+
static FcBool
-FcFreeTypeCheckGlyph (FT_Face face, FcChar32 ucs4,
+FcFreeTypeCheckGlyph (FT_Face face,
+ FcFreeTypeCheckGlyphInfo *info,
+ FcChar32 ucs4,
FT_UInt glyph, FcBlanks *blanks,
FT_Pos *advance,
FcBool using_strike)
{
FT_Int load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
- FT_GlyphSlot slot;
if (using_strike)
load_flags &= ~FT_LOAD_NO_SCALE;
@@ -2219,48 +2232,55 @@
if (face->face_flags & FT_FACE_FLAG_SCALABLE)
load_flags |= FT_LOAD_NO_BITMAP;
- if (FT_Load_Glyph (face, glyph, load_flags))
- return FcFalse;
+ /*
+ * Previously, we used to load glyphs here...
+ * If the load succeeded, then for bitmap fonts we were
+ * accepting, and for outline glyphs, we'd check whether
+ * the outline is non-empty...
+ *
+ * The new logic skips much of that and only checks for
+ * outline non-emptiness for TrueType outlines. We might
+ * want to add logic to load glyphs for bitmap-only fonts
+ * again. If not, there's a whole lot more cruft that can
+ * be removed...
+ */
- slot = face->glyph;
- if (!glyph)
- return FcFalse;
-
- *advance = slot->metrics.horiAdvance;
-
- switch ((int) slot->format) {
- case ft_glyph_format_bitmap:
- /*
- * Bitmaps are assumed to be reasonable; if
- * this proves to be a rash assumption, this
- * code can be easily modified
- */
- return FcTrue;
- case ft_glyph_format_outline:
- /*
- * Glyphs with contours are always OK
- */
- if (slot->outline.n_contours != 0)
- return FcTrue;
- /*
- * Glyphs with no contours are only OK if
- * they're members of the Blanks set specified
- * in the configuration. If blanks isn't set,
- * then allow any glyph to be blank
- */
- if (!blanks || FcBlanksIsMember (blanks, ucs4))
- return FcTrue;
- /* fall through ... */
- default:
- break;
+ if (advance)
+ {
+ *advance = 0;
+ FT_Get_Advance (face, glyph, load_flags, advance);
}
- return FcFalse;
+
+ if (info)
+ {
+ if (glyph >= info->num_glyphs)
+ return FcFalse;
+
+ if ((info->long_offset ?
+ info->offsets.u32[glyph] == info->offsets.u32[glyph+1] :
+ info->offsets.u16[glyph] == info->offsets.u16[glyph+1]))
+ {
+ /*
+ * Glyphs with no contours are only OK if
+ * they're members of the Blanks set specified
+ * in the configuration. If blanks isn't set,
+ * then allow any glyph to be blank
+ */
+ return !blanks || FcBlanksIsMember (blanks, ucs4);
+ }
+ }
+
+ return FcTrue;
}
#define APPROXIMATELY_EQUAL(x,y) (FC_ABS ((x) - (y)) <= FC_MAX (FC_ABS (x), FC_ABS (y)) / 33)
static FcCharSet *
-FcFreeTypeCharSetAndSpacingForSize (FT_Face face, FcBlanks *blanks, int *spacing, FT_Int strike_index)
+FcFreeTypeCharSetAndSpacingForSize (FT_Face face,
+ FcFreeTypeCheckGlyphInfo *info,
+ FcBlanks *blanks,
+ int *spacing,
+ FT_Int strike_index)
{
FcChar32 page, off, ucs4;
#ifdef CHECK
@@ -2300,7 +2320,7 @@
ucs4 = FT_Get_First_Char (face, &glyph);
while (glyph != 0)
{
- if (FcFreeTypeCheckGlyph (face, ucs4, glyph, blanks, &advance, using_strike))
+ if (FcFreeTypeCheckGlyph (face, info, ucs4, glyph, blanks, &advance, using_strike))
{
if (advance)
{
@@ -2386,7 +2406,7 @@
{
ucs4 = FcGlyphNameToUcs4 (name_buf);
if (ucs4 != 0xffff &&
- FcFreeTypeCheckGlyph (face, ucs4, glyph, blanks, &advance, using_strike))
+ FcFreeTypeCheckGlyph (face, info, ucs4, glyph, blanks, &advance, using_strike))
{
if (advance)
{
@@ -2429,7 +2449,7 @@
if (has_char && !has_bit)
{
- if (!FcFreeTypeCheckGlyph (face, ucs4, glyph, blanks, &advance, using_strike))
+ if (!FcFreeTypeCheckGlyph (face, info, ucs4, glyph, blanks, &advance, using_strike))
printf ("Bitmap missing broken char 0x%x\n", ucs4);
else
printf ("Bitmap missing char 0x%x\n", ucs4);
@@ -2455,28 +2475,63 @@
FcFreeTypeCharSetAndSpacing (FT_Face face, FcBlanks *blanks, int *spacing)
{
FcCharSet *cs;
+ FT_Int strike_index = -1;
+ TT_Header *head_table = FT_Get_Sfnt_Table (face, ft_sfnt_head);
+ FcFreeTypeCheckGlyphInfo *info = NULL;
- /*
- * Check for bitmap-only ttf fonts that are missing the glyf table.
- * In that case, pick a size and look for glyphs in that size instead
- */
- if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) &&
- face->num_fixed_sizes > 0 &&
- FT_Get_Sfnt_Table (face, ft_sfnt_head))
+ if (head_table)
{
- FT_Int strike_index = 0;
- int i;
+ /*
+ * Check for bitmap-only ttf fonts that are missing the glyf table.
+ * In that case, pick a size and look for glyphs in that size instead
+ */
+ if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && face->num_fixed_sizes > 0)
+ {
+ int i;
- /* Select the face closest to 16 pixels tall */
- for (i = 1; i < face->num_fixed_sizes; i++) {
- if (abs (face->available_sizes[i].height - 16) <
- abs (face->available_sizes[strike_index].height - 16))
- strike_index = i;
+ strike_index = 0;
+
+ /* Select the face closest to 16 pixels tall */
+ for (i = 1; i < face->num_fixed_sizes; i++) {
+ if (abs (face->available_sizes[i].height - 16) <
+ abs (face->available_sizes[strike_index].height - 16))
+ strike_index = i;
+ }
}
- cs = FcFreeTypeCharSetAndSpacingForSize (face, blanks, spacing, strike_index);
+ else if (head_table->Glyph_Data_Format == 0)
+ {
+ /* Try loading the 'loca' table, which we will later use to detect
+ * glyphs without outline (to reject them). If font is other than
+ * TrueType outlines, we bypass that check. */
+ unsigned int num_glyphs = face->num_glyphs;
+ FcBool long_offset = head_table->Index_To_Loc_Format > 0;
+ FT_ULong needed_len = (num_glyphs + 1) * (long_offset ? 4 : 2);
+ FT_ULong table_len = 0;
+ if (FT_Err_Ok == FT_Load_Sfnt_Table (face, TTAG_loca, 0, NULL, &table_len) &&
+ (table_len = FC_MIN(needed_len, table_len)) >= (long_offset ? 4 : 2) &&
+ (info = malloc (sizeof (*info) + table_len)))
+ {
+ if (FT_Err_Ok != FT_Load_Sfnt_Table (face, TTAG_loca, 0,
+ (FT_Byte *)&info->offsets, &table_len))
+ {
+ free (info);
+ info = NULL;
+ }
+ else
+ {
+ info->num_glyphs = FC_MIN(num_glyphs,
+ (table_len / (long_offset ? 4 : 2)) - 1);
+ info->long_offset = long_offset;
+ }
+ }
+ }
}
- else
- cs = FcFreeTypeCharSetAndSpacingForSize (face, blanks, spacing, -1);
+
+ cs = FcFreeTypeCharSetAndSpacingForSize (face, info, blanks, spacing, strike_index);
+
+ if (info)
+ free (info);
+
return cs;
}