bug 637481 - add vhea/vmtx support to OTS for CJK webfonts. r=jfkthame
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Thu, 24 Mar 2011 20:11:38 +0000
changeset 63900 60bfc795e935d8cc06025251e51f6af82907adcd
parent 63899 8887ebd9a6c86542499b8952bba1287f60d5d281
child 63901 b8fd81fa5a4b6fe29799e11d71f4c9e12741f83b
push idunknown
push userunknown
push dateunknown
reviewersjfkthame
bugs637481
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 637481 - add vhea/vmtx support to OTS for CJK webfonts. r=jfkthame
gfx/ots/src/hhea.cc
gfx/ots/src/hhea.h
gfx/ots/src/hmtx.cc
gfx/ots/src/ots.cc
gfx/ots/src/ots.h
gfx/thebes/gfxFontUtils.cpp
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxGDIFontList.cpp
--- a/gfx/ots/src/hhea.cc
+++ b/gfx/ots/src/hhea.cc
@@ -4,29 +4,32 @@
 
 #include "hhea.h"
 
 #include "head.h"
 #include "maxp.h"
 
 // hhea - Horizontal Header
 // http://www.microsoft.com/opentype/otspec/hhea.htm
+// vhea - Vertical Header
+// http://www.microsoft.com/opentype/otspec/vhea.htm
+// This file is used for both tables because they share the same structures.
 
 namespace ots {
 
-bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+bool ots_Xhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length,
+                    OpenTypeHHEA **out_hhea) {
   Buffer table(data, length);
   OpenTypeHHEA *hhea = new OpenTypeHHEA;
-  file->hhea = hhea;
+  *out_hhea = hhea;
 
-  uint32_t version = 0;
-  if (!table.ReadU32(&version)) {
+  if (!table.ReadU32(&hhea->version)) {
     return OTS_FAILURE();
   }
-  if (version >> 16 != 1) {
+  if (hhea->version >> 16 != 1) {
     return OTS_FAILURE();
   }
 
   if (!table.ReadS16(&hhea->ascent) ||
       !table.ReadS16(&hhea->descent) ||
       !table.ReadS16(&hhea->linegap) ||
       !table.ReadU16(&hhea->adv_width_max) ||
       !table.ReadS16(&hhea->min_lsb) ||
@@ -81,25 +84,34 @@ bool ots_hhea_parse(OpenTypeFile *file, 
 
   if (hhea->num_hmetrics > file->maxp->num_glyphs) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
+bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  return ots_Xhea_parse(file, data, length, &file->hhea);
+}
+
+bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  return ots_Xhea_parse(file, data, length, &file->vhea);
+}
+
 bool ots_hhea_should_serialise(OpenTypeFile *file) {
   return file->hhea;
 }
 
-bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
-  const OpenTypeHHEA *hhea = file->hhea;
+bool ots_vhea_should_serialise(OpenTypeFile *file) {
+  return file->preserve_otl && file->vhea;
+}
 
-  if (!out->WriteU32(0x00010000) ||
-      !out->WriteS16(hhea->ascent) ||
+bool ots_Xhea_serialise(OTSStream *out, OpenTypeFile *file, const OpenTypeHHEA *hhea) {
+  if (!out->WriteS16(hhea->ascent) ||
       !out->WriteS16(hhea->descent) ||
       !out->WriteS16(hhea->linegap) ||
       !out->WriteU16(hhea->adv_width_max) ||
       !out->WriteS16(hhea->min_lsb) ||
       !out->WriteS16(hhea->min_rsb) ||
       !out->WriteS16(hhea->x_max_extent) ||
       !out->WriteS16(hhea->caret_slope_rise) ||
       !out->WriteS16(hhea->caret_slope_run) ||
@@ -108,13 +120,31 @@ bool ots_hhea_serialise(OTSStream *out, 
       !out->WriteS16(0) ||  // metric data format
       !out->WriteU16(hhea->num_hmetrics)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
+bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->WriteU32(0x00010000)) {
+    return OTS_FAILURE();
+  }
+  return ots_Xhea_serialise(out, file, file->hhea);
+}
+
+bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->WriteU32(file->vhea->version)) {
+    return OTS_FAILURE();
+  }
+  return ots_Xhea_serialise(out, file, file->vhea);
+}
+
 void ots_hhea_free(OpenTypeFile *file) {
   delete file->hhea;
 }
 
+void ots_vhea_free(OpenTypeFile *file) {
+  delete file->vhea;
+}
+
 }  // namespace ots
--- a/gfx/ots/src/hhea.h
+++ b/gfx/ots/src/hhea.h
@@ -5,16 +5,17 @@
 #ifndef OTS_HHEA_H_
 #define OTS_HHEA_H_
 
 #include "ots.h"
 
 namespace ots {
 
 struct OpenTypeHHEA {
+  uint32_t version;
   int16_t ascent;
   int16_t descent;
   int16_t linegap;
   uint16_t adv_width_max;
   int16_t min_lsb;
   int16_t min_rsb;
   int16_t x_max_extent;
   int16_t caret_slope_rise;
--- a/gfx/ots/src/hmtx.cc
+++ b/gfx/ots/src/hmtx.cc
@@ -5,31 +5,35 @@
 #include <new>
 #include "hmtx.h"
 
 #include "hhea.h"
 #include "maxp.h"
 
 // hmtx - Horizontal Metrics
 // http://www.microsoft.com/opentype/otspec/hmtx.htm
+// vmtx - Vertical Metrics
+// http://www.microsoft.com/opentype/otspec/vmtx.htm
+// This file is used for both tables because they share the same structures.
 
 namespace ots {
 
-bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+bool ots_Xmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length,
+                    const OpenTypeHHEA *hhea, OpenTypeHMTX **out_hmtx) {
   Buffer table(data, length);
   OpenTypeHMTX *hmtx = new OpenTypeHMTX;
-  file->hmtx = hmtx;
+  *out_hmtx = hmtx;
 
-  if (!file->hhea || !file->maxp) {
+  if (!hhea || !file->maxp) {
     return OTS_FAILURE();
   }
 
   // |num_hmetrics| is a uint16_t, so it's bounded < 65536. This limits that
   // amount of memory that we'll allocate for this to a sane amount.
-  const unsigned num_hmetrics = file->hhea->num_hmetrics;
+  const unsigned num_hmetrics = hhea->num_hmetrics;
 
   if (num_hmetrics > file->maxp->num_glyphs) {
     return OTS_FAILURE();
   }
   if (!num_hmetrics) {
     return OTS_FAILURE();
   }
   const unsigned num_lsbs = file->maxp->num_glyphs - num_hmetrics;
@@ -40,69 +44,91 @@ bool ots_hmtx_parse(OpenTypeFile *file, 
     int16_t lsb = 0;
     if (!table.ReadU16(&adv) || !table.ReadS16(&lsb)) {
       return OTS_FAILURE();
     }
 
     // Since so many fonts don't have proper value on |adv| and |lsb|,
     // we should not call ots_failure() here. For example, about 20% of fonts
     // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
-    if (adv > file->hhea->adv_width_max) {
-      OTS_WARNING("bad adv: %u > %u", adv, file->hhea->adv_width_max);
-      adv = file->hhea->adv_width_max;
+    if (adv > hhea->adv_width_max) {
+      OTS_WARNING("bad adv: %u > %u", adv, hhea->adv_width_max);
+      adv = hhea->adv_width_max;
     }
-    if (lsb < file->hhea->min_lsb) {
-      OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
-      lsb = file->hhea->min_lsb;
+    if (lsb < hhea->min_lsb) {
+      OTS_WARNING("bad lsb: %d < %d", lsb, hhea->min_lsb);
+      lsb = hhea->min_lsb;
     }
 
     hmtx->metrics.push_back(std::make_pair(adv, lsb));
   }
 
   hmtx->lsbs.reserve(num_lsbs);
   for (unsigned i = 0; i < num_lsbs; ++i) {
     int16_t lsb;
     if (!table.ReadS16(&lsb)) {
       // Some Japanese fonts (e.g., mona.ttf) fail this test.
       return OTS_FAILURE();
     }
 
-    if (lsb < file->hhea->min_lsb) {
+    if (lsb < hhea->min_lsb) {
       // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
       // (e.g., Notice2Std.otf) have weird lsb values.
-      OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
-      lsb = file->hhea->min_lsb;
+      OTS_WARNING("bad lsb: %d < %d", lsb, hhea->min_lsb);
+      lsb = hhea->min_lsb;
     }
 
     hmtx->lsbs.push_back(lsb);
   }
 
   return true;
 }
 
+bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  return ots_Xmtx_parse(file, data, length, file->hhea, &file->hmtx);
+}
+
+bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  return ots_Xmtx_parse(file, data, length, file->vhea, &file->vmtx);
+}
+
 bool ots_hmtx_should_serialise(OpenTypeFile *file) {
   return file->hmtx;
 }
 
-bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
-  const OpenTypeHMTX *hmtx = file->hmtx;
+bool ots_vmtx_should_serialise(OpenTypeFile *file) {
+  return file->preserve_otl && file->vmtx;
+}
 
+bool ots_Xmtx_serialise(OTSStream *out, OpenTypeFile *file, const OpenTypeHMTX *hmtx) {
   for (unsigned i = 0; i < hmtx->metrics.size(); ++i) {
     if (!out->WriteU16(hmtx->metrics[i].first) ||
         !out->WriteS16(hmtx->metrics[i].second)) {
       return OTS_FAILURE();
     }
   }
 
   for (unsigned i = 0; i < hmtx->lsbs.size(); ++i) {
     if (!out->WriteS16(hmtx->lsbs[i])) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
+bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  return ots_Xmtx_serialise(out, file, file->hmtx);
+}
+
+bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  return ots_Xmtx_serialise(out, file, file->vmtx);
+}
+
 void ots_hmtx_free(OpenTypeFile *file) {
   delete file->hmtx;
 }
 
+void ots_vmtx_free(OpenTypeFile *file) {
+  delete file->vmtx;
+}
+
 }  // namespace ots
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -137,16 +137,20 @@ const struct {
   { Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise,
     ots::ots_prep_should_serialise, ots::ots_prep_free, false },
   { Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
     ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
   { Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise,
     ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
   { Tag("kern"), ots::ots_kern_parse, ots::ots_kern_serialise,
     ots::ots_kern_should_serialise, ots::ots_kern_free, false },
+  { Tag("vhea"), ots::ots_vhea_parse, ots::ots_vhea_serialise,
+    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
+  { Tag("vmtx"), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
   { Tag("GDEF"), ots::ots_gdef_parse, ots::ots_gdef_serialise,
     ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
   { Tag("GPOS"), ots::ots_gpos_parse, ots::ots_gpos_serialise,
     ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
   { Tag("GSUB"), ots::ots_gsub_parse, ots::ots_gsub_serialise,
     ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
   // TODO(yusukes): Support GDEF, GPOS, GSUB, mort, base, and jstf tables.
   { 0, NULL, NULL, NULL, NULL, false }
--- a/gfx/ots/src/ots.h
+++ b/gfx/ots/src/ots.h
@@ -172,25 +172,30 @@ class Buffer {
   F(loca, LOCA) \
   F(ltsh, LTSH) \
   F(maxp, MAXP) \
   F(name, NAME) \
   F(os2, OS2) \
   F(post, POST) \
   F(prep, PREP) \
   F(vdmx, VDMX) \
+  F(vhea, VHEA) \
+  F(vmtx, VMTX) \
   F(vorg, VORG) \
   F(gdef, GDEF) \
   F(gpos, GPOS) \
   F(gsub, GSUB)
 
 #define F(name, capname) struct OpenType##capname;
 FOR_EACH_TABLE_TYPE
 #undef F
 
+#define OpenTypeVHEA OpenTypeHHEA
+#define OpenTypeVMTX OpenTypeHMTX
+
 struct OpenTypeFile {
   OpenTypeFile() {
 #define F(name, capname) name = NULL;
     FOR_EACH_TABLE_TYPE
 #undef F
   }
 
   uint32_t version;
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -2156,17 +2156,30 @@ gfxFontUtils::MakeEOTHeader(const PRUint
 
     // DumpEOTHeader(aHeader->Elements(), aHeader->Length());
 
     return NS_OK;
 }
 
 /* static */
 PRBool
-gfxFontUtils::IsCffFont(const PRUint8* aFontData)
+gfxFontUtils::IsCffFont(const PRUint8* aFontData, PRBool& hasVertical)
 {
     // this is only called after aFontData has passed basic validation,
     // so we know there is enough data present to allow us to read the version!
     const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+
+    PRUint32 i;
+    PRUint32 numTables = sfntHeader->numTables;
+    const TableDirEntry *dirEntry = 
+        reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
+    hasVertical = PR_FALSE;
+    for (i = 0; i < numTables; i++, dirEntry++) {
+        if (dirEntry->tag == TRUETYPE_TAG('v','h','e','a')) {
+            hasVertical = PR_TRUE;
+            break;
+        }
+    }
+
     return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O'));
 }
 
 #endif
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -691,17 +691,17 @@ public:
     // EOT header on output
     static nsresult
     MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
                   FallibleTArray<PRUint8> *aHeader, FontDataOverlay *aOverlay);
 
     // determine whether a font (which has already passed ValidateSFNTHeaders)
     // is CFF format rather than TrueType
     static PRBool
-    IsCffFont(const PRUint8* aFontData);
+    IsCffFont(const PRUint8* aFontData, PRBool& hasVertical);
 
 #endif
 
     // determine the format of font data
     static gfxUserFontType
     DetermineFontDataType(const PRUint8 *aFontData, PRUint32 aFontDataLength);
 
     // checks for valid SFNT table structure, returns true if valid
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -875,18 +875,19 @@ gfxGDIFontList::MakePlatformFont(const g
         const PRUint8 *mFontData;
     };
     FontDataDeleter autoDelete(aFontData);
 
     // if calls aren't available, bail
     if (!TTLoadEmbeddedFontPtr || !TTDeleteEmbeddedFontPtr)
         return nsnull;
 
-    PRBool isCFF = gfxFontUtils::IsCffFont(aFontData);
-        
+    PRBool hasVertical;
+    PRBool isCFF = gfxFontUtils::IsCffFont(aFontData, hasVertical);
+
     nsresult rv;
     HANDLE fontRef = nsnull;
     PRBool isEmbedded = PR_FALSE;
 
     nsAutoString uniqueName;
     rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
     if (NS_FAILED(rv))
         return nsnull;
@@ -952,17 +953,19 @@ gfxGDIFontList::MakePlatformFont(const g
         // "A font that is added by AddFontMemResourceEx is always private 
         //  to the process that made the call and is not enumerable."
         fontRef = AddFontMemResourceEx(fontData, fontLength, 
                                        0 /* reserved */, &numFonts);
         if (!fontRef)
             return nsnull;
 
         // only load fonts with a single face contained in the data
-        if (fontRef && numFonts != 1) {
+        // AddFontMemResourceEx generates an additional face name for
+        // vertical text if the font supports vertical writing
+        if (fontRef && numFonts != 1 + !!hasVertical) {
             RemoveFontMemResourceEx(fontRef);
             return nsnull;
         }
     }
 
     // make a new font entry using the unique name
     WinUserFontData *winUserFontData = new WinUserFontData(fontRef, isEmbedded);
     PRUint16 w = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight);