Add support for extracting kerning tables from 'kern' tables. Only format 0
authorben <ben@cda61777-01e9-0310-a592-d414129be87e>
Sat, 10 Feb 2007 22:34:44 +0000 (22:34 +0000)
committerben <ben@cda61777-01e9-0310-a592-d414129be87e>
Sat, 10 Feb 2007 22:34:44 +0000 (22:34 +0000)
(simple pair kerning) is supported, but with both Apple- and Microsoft-format
headers.

Also reference Microsoft's TrueType specification in comments, since I've used
that to write some of this code.

git-svn-id: svn://svn.tartarus.org/sgt/halibut@7268 cda61777-01e9-0310-a592-d414129be87e

in_sfnt.c

index b33a2a1..4eeabb6 100644 (file)
--- a/in_sfnt.c
+++ b/in_sfnt.c
@@ -12,6 +12,9 @@
  *
  * The TrueType Reference Manual:
  * <http://developer.apple.com/textfonts/TTRefMan/>
+ *
+ * Microsoft typography specifications:
+ * <http://www.microsoft.com/typography/SpecificationsOverview.mspx>
  */
 
 #include <assert.h>
@@ -127,6 +130,7 @@ sfnt_decode offsubdir_decode[] = {
 #define TAG_head       0x68656164
 #define TAG_hhea       0x68686561
 #define TAG_hmtx       0x686d7478
+#define TAG_kern       0x6b65726e
 #define TAG_loca       0x6c6f6361
 #define TAG_name       0x6e616d65
 #define TAG_post       0x706f7374
@@ -288,6 +292,78 @@ sfnt_decode longhormetric_decode[] = {
     { d_end }
 };
 
+/* Kerning ('kern') table */
+typedef struct t_kern_Tag t_kern;
+struct t_kern_Tag {
+    unsigned version;
+    unsigned nTables;
+};
+sfnt_decode t_kern_v0_decode[] = {
+    { d_uint16, offsetof(t_kern, version) },
+    { d_uint16, offsetof(t_kern, nTables) },
+    { d_end }
+};
+typedef struct kern_v0_subhdr_Tag kern_v0_subhdr;
+struct kern_v0_subhdr_Tag {
+    unsigned version;
+    unsigned length;
+    unsigned coverage;
+};
+sfnt_decode kern_v0_subhdr_decode[] = {
+    { d_uint16, offsetof(kern_v0_subhdr, version) },
+    { d_uint16, offsetof(kern_v0_subhdr, length) },
+    { d_uint16, offsetof(kern_v0_subhdr, coverage) },
+    { d_end }
+};
+#define KERN_V0_HORIZ          0x0001
+#define KERN_V0_MINIMUM                0x0002
+#define KERN_V0_CROSSSTREAM    0x0004
+#define KERN_V0_OVERRIDE       0x0008
+#define KERN_V0_FORMAT         0xff00
+#define KERN_V0_FORMAT_0       0x0000
+sfnt_decode t_kern_v1_decode[] = {
+    { d_uint32, offsetof(t_kern, version) },
+    { d_uint32, offsetof(t_kern, nTables) },
+    { d_end }
+};
+typedef struct kern_v1_subhdr_Tag kern_v1_subhdr;
+struct kern_v1_subhdr_Tag {
+    unsigned length;
+    unsigned coverage;
+};
+sfnt_decode kern_v1_subhdr_decode[] = {
+    { d_uint32, offsetof(kern_v1_subhdr, length) },
+    { d_uint16, offsetof(kern_v1_subhdr, coverage) },
+    { d_skip(2) }, /* tupleIndex */
+    { d_end }
+};
+#define KERN_V1_VERTICAL       0x8000
+#define KERN_V1_CROSSSTREAM    0x4000
+#define KERN_V1_VARIATION      0x2000
+#define KERN_V1_FORMAT         0x00ff
+#define KERN_V1_FORMAT_0       0x0000
+typedef struct kern_f0_Tag kern_f0;
+struct kern_f0_Tag {
+    unsigned nPairs;
+};
+sfnt_decode kern_f0_decode[] = {
+    { d_uint16, offsetof(kern_f0, nPairs) },
+    { d_skip(6) }, /* searchRange, entrySelector, rangeShift */
+    { d_end }
+};
+typedef struct kern_f0_pair_Tag kern_f0_pair;
+struct kern_f0_pair_Tag {
+    unsigned left;
+    unsigned right;
+    int value;
+};
+sfnt_decode kern_f0_pair_decode[] = {
+    { d_uint16, offsetof(kern_f0_pair, left) },
+    { d_uint16, offsetof(kern_f0_pair, right) },
+    { d_int16, offsetof(kern_f0_pair, value) },
+    { d_end }
+};
+
 /* Naming ('name') table  */
 typedef struct t_name_Tag t_name;
 typedef struct namerecord_Tag namerecord;
@@ -570,6 +646,72 @@ void sfnt_getmetrics(font_info *fi) {
 }
 
 /*
+ * Get kerning data from a 'kern' table
+ *
+ * 'kern' tables have two gratuitously different header formats, one
+ * used by Apple and one by Microsoft.  Happily, the kerning tables
+ * themselves use the same formats.  Halibut only supports simple kern
+ * pairs for horizontal kerning of horizontal text, and ignores
+ * everything else.
+ */
+static void sfnt_getkern(font_info *fi) {
+    sfnt *sf = fi->fontfile;
+    t_kern kern;
+    unsigned version, i, j;
+    void *ptr, *end;
+
+    if (!sfnt_findtable(sf, TAG_kern, &ptr, &end))
+       return;
+    if (!decode(uint16_decode, ptr, end, &version))
+       return;
+    if (version == 0)
+       ptr = decode(t_kern_v0_decode, ptr, end, &kern);
+    else if (version == 1)
+       ptr = decode(t_kern_v1_decode, ptr, end, &kern);
+    else return;
+    if (ptr == NULL) return;
+    for (i = 0; i < kern.nTables; i++) {
+       kern_f0 f0;
+       kern_pair *kerns;
+       if (version == 0) {
+           kern_v0_subhdr sub;
+           ptr = decode(kern_v0_subhdr_decode, ptr, end, &sub);
+           if (ptr == NULL) return;
+           if (sub.version != 0 ||
+               (sub.coverage & (KERN_V0_HORIZ | KERN_V0_MINIMUM |
+                                KERN_V0_CROSSSTREAM | KERN_V0_FORMAT)) !=
+               (KERN_V0_HORIZ | KERN_V0_FORMAT_0)) {
+               ptr = (char *)ptr + sub.length - 6;
+               continue;
+           }
+       } else {
+           kern_v1_subhdr sub;
+           ptr = decode(kern_v1_subhdr_decode, ptr, end, &sub);
+           if (ptr == NULL) return;
+           if ((sub.coverage & (KERN_V1_VERTICAL | KERN_V1_CROSSSTREAM |
+                               KERN_V1_VARIATION | KERN_V1_FORMAT)) !=
+               KERN_V0_FORMAT_0) {
+               ptr = (char *)ptr + sub.length - 8;
+               continue;
+           }
+       }
+       ptr = decode(kern_f0_decode, ptr, end, &f0);
+       if (ptr == NULL) return;
+       kerns = snewn(f0.nPairs, kern_pair);
+       for (j = 0; j < f0.nPairs; j++) {
+           kern_f0_pair p;
+           kern_pair *kp = kerns + j;
+           ptr = decode(kern_f0_pair_decode, ptr, end, &p);
+           if (ptr == NULL) return;
+           kp->left = sfnt_indextoglyph(sf, p.left);
+           kp->right = sfnt_indextoglyph(sf, p.right);
+           kp->kern = p.value * UNITS_PER_PT / (int)sf->head.unitsPerEm;
+           add234(fi->kerns, kp);
+       }
+    }
+}
+
+/*
  * Get mapping data from 'cmap' table
  *
  * We look for either a (0, 3), or (3, 1) table, both of these being
@@ -708,6 +850,7 @@ void read_sfnt_file(input *in) {
     fi->name = sfnt_psname(fi);
     sfnt_mapglyphs(fi);
     sfnt_getmetrics(fi);
+    sfnt_getkern(fi);
     sfnt_getmap(fi);
     fi->next = all_fonts;
     all_fonts = fi;