From c465740273f690510f86bce864c379c4c26c0382 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 12 Sep 2023 10:37:55 +0800 Subject: [PATCH] Extract font foundry metadata from the OS/2 table * src/sfnt.c (sfnt_table_names): Append an entry for OS/2 tables. (sfnt_read_OS_2_table): New function. (main): Introduce a new test for the OS/2 table reader. * src/sfnt.h (sfnt_read_OS_2_table): New declaration. * src/sfntfont.c (sfnt_decode_foundry_name): Delete function. (sfnt_enum_font_1): Read the font's OS/2 table and extract the foundry name from there. Use `misc' if absent. (sfntfont_desc_to_entity, sfntfont_open): Cease interning desc->designer, as that is now a symbol. (syms_of_sfntfont) : New defsym Qmisc. (bug#65865) --- src/sfnt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sfnt.h | 88 ++++++++++++++++++++++++++++++++++++++ src/sfntfont.c | 74 ++++++++++++++++---------------- 3 files changed, 237 insertions(+), 37 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index ae7660a456e..c0ab1e6587d 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -157,6 +157,7 @@ static uint32_t sfnt_table_names[] = [SFNT_TABLE_GVAR] = 0x67766172, [SFNT_TABLE_CVAR] = 0x63766172, [SFNT_TABLE_AVAR] = 0x61766172, + [SFNT_TABLE_OS_2] = 0x4f532f32, }; /* Swap values from TrueType to system byte order. */ @@ -15296,6 +15297,110 @@ sfnt_vary_interpreter (struct sfnt_interpreter *interpreter, +/* OS/2 metadata retrieval. + + A font's `OS/2' table incorporates some miscellaneous information + that is consulted by the font scaler on MS-Windows. Emacs requires + one fragment of this information: the font foundry name. */ + +/* Read an OS/2 table from the given font FD. Use the table directory + provided in SUBTABLE. + + Return the OS/2 table if successful, NULL otherwise. */ + +TEST_STATIC struct sfnt_OS_2_table * +sfnt_read_OS_2_table (int fd, struct sfnt_offset_subtable *subtable) +{ + struct sfnt_OS_2_table *OS_2; + struct sfnt_table_directory *directory; + ssize_t rc; + size_t minimum, wanted; + + /* Search for the OS/2 table within SUBTABLE. */ + + directory = sfnt_find_table (subtable, SFNT_TABLE_OS_2); + + if (!directory) + return NULL; + + /* Calculate how large the table must be. The field `panose' is the + last field aligned to natural boundaries, and thus contents must + be read twice: once to populate the table with information up to + `panose', and once again to retrieve the information + afterwards. */ + + minimum = (SFNT_ENDOF (struct sfnt_OS_2_table, panose, + unsigned char[10]) + + SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index, + uint16_t) + - offsetof (struct sfnt_OS_2_table, ul_unicode_range)); + + /* If the table is too short, return. */ + if (directory->length < minimum) + return NULL; + + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; + + OS_2 = xmalloc (sizeof *OS_2); + + /* Read data up to the end of `panose'. */ + + wanted = SFNT_ENDOF (struct sfnt_OS_2_table, panose, + unsigned char[10]); + rc = read (fd, OS_2, wanted); + + if (rc != wanted) + { + xfree (OS_2); + return NULL; + } + + /* Byte swap that data. */ + + sfnt_swap16 (&OS_2->version); + sfnt_swap16 (&OS_2->x_avg_char_width); + sfnt_swap16 (&OS_2->us_weight_class); + sfnt_swap16 (&OS_2->us_width_class); + sfnt_swap16 (&OS_2->fs_type); + sfnt_swap16 (&OS_2->y_subscript_x_size); + sfnt_swap16 (&OS_2->y_subscript_y_size); + sfnt_swap16 (&OS_2->y_subscript_x_offset); + sfnt_swap16 (&OS_2->y_subscript_y_offset); + sfnt_swap16 (&OS_2->y_superscript_x_size); + sfnt_swap16 (&OS_2->y_superscript_y_size); + sfnt_swap16 (&OS_2->y_superscript_x_offset); + sfnt_swap16 (&OS_2->y_superscript_y_offset); + sfnt_swap16 (&OS_2->y_strikeout_size); + sfnt_swap16 (&OS_2->y_strikeout_position); + sfnt_swap16 (&OS_2->s_family_class); + + /* Read fields between ul_unicode_range and fs_last_char_index. */ + wanted = (SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index, + uint16_t) + - offsetof (struct sfnt_OS_2_table, ul_unicode_range)); + rc = read (fd, &OS_2->ul_unicode_range, wanted); + + if (rc != wanted) + { + xfree (OS_2); + return NULL; + } + + /* Swap the remainder and return the table. */ + sfnt_swap32 (&OS_2->ul_unicode_range[0]); + sfnt_swap32 (&OS_2->ul_unicode_range[1]); + sfnt_swap32 (&OS_2->ul_unicode_range[2]); + sfnt_swap32 (&OS_2->ul_unicode_range[3]); + sfnt_swap16 (&OS_2->fs_selection); + sfnt_swap16 (&OS_2->fs_first_char_index); + sfnt_swap16 (&OS_2->fs_last_char_index); + return OS_2; +} + + + #ifdef TEST struct sfnt_test_dcontext @@ -19158,6 +19263,7 @@ main (int argc, char **argv) struct sfnt_gvar_table *gvar; struct sfnt_avar_table *avar; struct sfnt_cvar_table *cvar; + struct sfnt_OS_2_table *OS_2; sfnt_fixed scale; char *fancy; int *advances; @@ -19293,6 +19399,7 @@ main (int argc, char **argv) fvar = sfnt_read_fvar_table (fd, font); gvar = sfnt_read_gvar_table (fd, font); avar = sfnt_read_avar_table (fd, font); + OS_2 = sfnt_read_OS_2_table (fd, font); cvar = NULL; hmtx = NULL; @@ -19309,6 +19416,10 @@ main (int argc, char **argv) loca_long = NULL; loca_short = NULL; + if (OS_2) + fprintf (stderr, "OS/2 table found!\nach_vendor_id: %.4s\n", + OS_2->ach_vendor_id); + if (fvar) { fprintf (stderr, "FVAR table found!\n" @@ -19971,6 +20082,7 @@ main (int argc, char **argv) xfree (gvar); xfree (avar); xfree (cvar); + xfree (OS_2); return 0; } diff --git a/src/sfnt.h b/src/sfnt.h index 1a6b2209abc..6602d240051 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -52,6 +52,7 @@ enum sfnt_table SFNT_TABLE_GVAR, SFNT_TABLE_CVAR, SFNT_TABLE_AVAR, + SFNT_TABLE_OS_2, }; #define SFNT_ENDOF(type, field, type1) \ @@ -1333,6 +1334,85 @@ struct sfnt_metrics_distortion +/* OS/2 font metadata. */ + +struct sfnt_OS_2_table +{ + /* Table version number. */ + uint16_t version; + + /* Average weighted advance width of lower case letters and + space. */ + int16_t x_avg_char_width; + + /* Wisual weight (degree of blackness or thickness) of stroke in + glyphs. */ + uint16_t us_weight_class; + + /* Relative change from the normal aspect ratio (width to height + ratio) as specified by a font designer for the glyphs in the + font. */ + uint16_t us_width_class; + + /* Miscellaneous font attributes. */ + int16_t fs_type; + + /* Recommended horizontal size in pixels for subscripts. */ + int16_t y_subscript_x_size; + + /* Recommended vertical subscript size. */ + int16_t y_subscript_y_size; + + /* Recommended horizontal offset for subscripts. */ + int16_t y_subscript_x_offset; + + /* Recommended vertical offset from the baseline for subscripts. */ + int16_t y_subscript_y_offset; + + /* Recommended horizontal size in pixels for superscripts. */ + int16_t y_superscript_x_size; + + /* Recommended vertical superscript size. */ + int16_t y_superscript_y_size; + + /* Recommended horizontal offset for superscripts. */ + int16_t y_superscript_x_offset; + + /* Recommended vertical offset from the baseline for superscripts. */ + int16_t y_superscript_y_offset; + + /* Width of the strikeout stroke. */ + int16_t y_strikeout_size; + + /* Position of the strikeout stroke relative to the baseline. */ + int16_t y_strikeout_position; + + /* Font family classification. */ + int16_t s_family_class; + + /* Microsoft ``panose'' classification. */ + unsigned char panose[10]; + + /* Alignment boundary! */ + + /* Unicode range specification. */ + uint32_t ul_unicode_range[4]; + + /* Font foundry name. */ + char ach_vendor_id[4]; + + /* Two byte bitfield providing the nature of font patterns. */ + uint16_t fs_selection; + + /* The minimum Unicode codepoint covered. */ + uint16_t fs_first_char_index; + + /* The maximum Unicode codepoint covered. */ + uint16_t fs_last_char_index; +}; + + + #define SFNT_CEIL_FIXED(fixed) (((fixed) + 0177777) & 037777600000) #define SFNT_FLOOR_FIXED(fixed) ((fixed) & 037777600000) @@ -1500,6 +1580,14 @@ extern int sfnt_vary_compound_glyph (struct sfnt_blend *, sfnt_glyph, struct sfnt_glyph *, struct sfnt_metrics_distortion *); + + +#define PROTOTYPE int, struct sfnt_offset_subtable * + +extern struct sfnt_OS_2_table *sfnt_read_OS_2_table (PROTOTYPE); + +#undef PROTOTYPE + #endif /* TEST */ diff --git a/src/sfntfont.c b/src/sfntfont.c index 12fecb32df5..fb0a2546864 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -109,7 +109,7 @@ struct sfnt_font_desc /* Style name of the font. */ Lisp_Object style; - /* Designer (foundry) of the font. */ + /* The font foundry name, or `misc' if not present. */ Lisp_Object designer; /* Style tokens that could not be parsed. */ @@ -365,28 +365,6 @@ sfnt_decode_family_style (struct sfnt_name_table *name, return (!NILP (*family) && !NILP (*style)) ? 0 : 1; } -/* Decode the foundry names from the name table NAME. Return the - foundry name, or nil upon failure. */ - -static Lisp_Object -sfnt_decode_foundry_name (struct sfnt_name_table *name) -{ - struct sfnt_name_record designer_rec; - unsigned char *designer_data; - - designer_data = sfnt_find_name (name, SFNT_NAME_DESIGNER, - &designer_rec); - - if (!designer_data) - return Qnil; - - return sfnt_decode_font_string (designer_data, - designer_rec.platform_id, - designer_rec.platform_specific_id, - designer_rec.language_id, - designer_rec.length); -} - /* Decode the name of the specified font INSTANCE using the given NAME table. Return the name of that instance, or nil upon failure. */ @@ -972,9 +950,11 @@ sfnt_enum_font_1 (int fd, const char *file, struct sfnt_meta_table *meta; struct sfnt_maxp_table *maxp; struct sfnt_fvar_table *fvar; + struct sfnt_OS_2_table *OS_2; struct sfnt_font_desc temp; Lisp_Object family, style, instance, style1; int i; + char buffer[5]; /* Create the font desc and copy in the file name. */ desc = xzalloc (sizeof *desc + strlen (file) + 1); @@ -1013,10 +993,33 @@ sfnt_enum_font_1 (int fd, const char *file, /* Set the family. */ desc->family = family; - desc->designer = sfnt_decode_foundry_name (name); desc->char_cache = Qnil; desc->subtable.platform_id = 500; + /* Now set the font foundry name. This information is located + within the OS/2 table's `ach_vendor_id' field, but use `misc' as + a recourse if it is not present. */ + + OS_2 = sfnt_read_OS_2_table (fd, subtables); + + if (OS_2) + { + memcpy (buffer, OS_2->ach_vendor_id, + sizeof OS_2->ach_vendor_id); + buffer[sizeof OS_2->ach_vendor_id] = '\0'; + + /* If the foundry name is empty, use `misc' instead. */ + + if (!buffer[0]) + desc->designer = Qmisc; + else + desc->designer = intern (buffer); + + xfree (OS_2); + } + else + desc->designer = Qmisc; + /* Set the largest glyph identifier. */ desc->num_glyphs = maxp->num_glyphs; @@ -1843,11 +1846,7 @@ sfntfont_desc_to_entity (struct sfnt_font_desc *desc, int instance) entity = font_make_entity (); ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name); - - if (!NILP (desc->designer)) - ASET (entity, FONT_FOUNDRY_INDEX, - Fintern (desc->designer, Qnil)); - + ASET (entity, FONT_FOUNDRY_INDEX, desc->designer); ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil)); ASET (entity, FONT_ADSTYLE_INDEX, Qnil); ASET (entity, FONT_REGISTRY_INDEX, @@ -3186,10 +3185,12 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, /* Figure out the font ascent and descent. */ font->ascent = ceil (font_info->hhea->ascent - * pixel_size * 1.0 / font_info->head->units_per_em); + * pixel_size + * (1.0 / font_info->head->units_per_em)); font->descent - = -floor (font_info->hhea->descent - * pixel_size * 1.0 / font_info->head->units_per_em); + = ceil ((-font_info->hhea->descent) + * pixel_size + * (1.0 / font_info->head->units_per_em)); font->height = font->ascent + font->descent; /* Set font->max_width to the maximum advance width. */ @@ -3198,11 +3199,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, /* Set generic attributes such as type and style. */ ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name); - - if (!NILP (desc->designer)) - ASET (font_object, FONT_FOUNDRY_INDEX, - Fintern (desc->designer, Qnil)); - + ASET (font_object, FONT_FOUNDRY_INDEX, desc->designer); ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil)); ASET (font_object, FONT_ADSTYLE_INDEX, Qnil); ASET (font_object, FONT_REGISTRY_INDEX, @@ -3956,6 +3953,9 @@ syms_of_sfntfont (void) /* Char-table purpose. */ DEFSYM (Qfont_lookup_cache, "font-lookup-cache"); + /* Default foundry name. */ + DEFSYM (Qmisc, "misc"); + /* Set up staticpros. */ sfnt_vendor_name = Qnil; staticpro (&sfnt_vendor_name); -- 2.39.5