]> git.eshelyaron.com Git - emacs.git/commitdiff
Extract font foundry metadata from the OS/2 table
authorPo Lu <luangruo@yahoo.com>
Tue, 12 Sep 2023 02:37:55 +0000 (10:37 +0800)
committerPo Lu <luangruo@yahoo.com>
Tue, 12 Sep 2023 02:37:55 +0000 (10:37 +0800)
* 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) <Qmisc>: New defsym Qmisc.  (bug#65865)

src/sfnt.c
src/sfnt.h
src/sfntfont.c

index ae7660a456eb12318cc0ae3c3f8b9a7b82203320..c0ab1e6587d0fa2b3d3c56ff984e92873ed427e2 100644 (file)
@@ -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,
 
 \f
 
+/* 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;
+}
+
+\f
+
 #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;
 }
index 1a6b2209abc9f506259fb6cc3261dd41ec49e81f..6602d2400517d60157a2a5a2ebb58cee691454dd 100644 (file)
@@ -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
 
 \f
 
+/* 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;
+};
+
+\f
+
 #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 *);
 
+\f
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_OS_2_table *sfnt_read_OS_2_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
 #endif /* TEST */
 
 \f
index 12fecb32df5abbbcb73fdf15a629742bc3becd79..fb0a2546864fdc309edd692d9e6289daa5e24941 100644 (file)
@@ -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);