summaryrefslogtreecommitdiffstats
path: root/skia
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-21 23:45:49 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-21 23:45:49 +0000
commit0dcb16bc3f6baf94d9b87a8685bc8309127018ad (patch)
treea9d497382b67ecc987327d62dad2c707db6bd5d5 /skia
parent17e09ae6e699f59553f8e70c0949f7302058bde5 (diff)
downloadchromium_src-0dcb16bc3f6baf94d9b87a8685bc8309127018ad.zip
chromium_src-0dcb16bc3f6baf94d9b87a8685bc8309127018ad.tar.gz
chromium_src-0dcb16bc3f6baf94d9b87a8685bc8309127018ad.tar.bz2
Parse VDMX tables for font metrics in Skia.
Although you cannot see the blood, sweat and tears on this changelist, know that they are there. We still aren't quite perfect, but it's better. Review URL: http://codereview.chromium.org/11344 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5868 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia')
-rw-r--r--skia/SConscript1
-rw-r--r--skia/include/SkPaint.h6
-rw-r--r--skia/ports/SkFontHost_FreeType.cpp60
-rw-r--r--skia/ports/SkFontHost_TrueType_VDMX.cpp189
4 files changed, 256 insertions, 0 deletions
diff --git a/skia/SConscript b/skia/SConscript
index ad1f194..97ea1be 100644
--- a/skia/SConscript
+++ b/skia/SConscript
@@ -160,6 +160,7 @@ if env['PLATFORM'] == 'posix':
input_files.remove('ports/SkFontHost_none.cpp')
input_files.append('sgl/SkTypeface.cpp')
input_files.append('ports/SkFontHost_FreeType.cpp')
+ input_files.append('ports/SkFontHost_TrueType_VDMX.cpp')
input_files.append('ports/SkFontHost_gamma_none.cpp')
input_files.append('ports/SkFontHost_fontconfig.cpp')
input_files.append('images/SkMMapStream.cpp')
diff --git a/skia/include/SkPaint.h b/skia/include/SkPaint.h
index 73e3dbd..06009cb 100644
--- a/skia/include/SkPaint.h
+++ b/skia/include/SkPaint.h
@@ -587,6 +587,12 @@ public:
SkScalar fBottom; //!< The greatest distance below the baseline for any glyph (will be >= 0)
SkScalar fLeading; //!< The recommended distance to add between lines of text (will be >= 0)
SkScalar fHeight; //!< the vertical distance between two consecutive baselines (>= 0)
+
+ // VDMX values are exact ascent and descent values for scalable fonts at
+ // a certain pixel size.
+ bool fVDMXMetricsValid; //!< If true, the following members are valid
+ unsigned fVDMXAscent;
+ unsigned fVDMXDescent;
};
/** Return the recommend spacing between lines (which will be
diff --git a/skia/ports/SkFontHost_FreeType.cpp b/skia/ports/SkFontHost_FreeType.cpp
index 2a2444e..172e296 100644
--- a/skia/ports/SkFontHost_FreeType.cpp
+++ b/skia/ports/SkFontHost_FreeType.cpp
@@ -31,6 +31,7 @@
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_SIZES_H
+#include FT_TRUETYPE_TABLES_H
//#define ENABLE_GLYPH_SPEW // for tracing calls to generateMetrics/generateImage
//#define DUMP_STRIKE_CREATION
@@ -723,6 +724,51 @@ void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, SkPath* path)
path->close();
}
+// -----------------------------------------------------------------------------
+// This is an extern from SkFontHost_TrueType_VDMX. See comments there in for
+// details of the arguments.
+// -----------------------------------------------------------------------------
+extern bool VDMX_Parse(int* ymax, int* ymin, const uint8_t* vdmx,
+ const size_t vdmx_length, const unsigned target_pelsize);
+
+// -----------------------------------------------------------------------------
+// Attempt to load and parse a VDMX table from the given face, extracting a
+// ascender and descender values for the given pelsize.
+// ymax: (output) the ascender value from the table
+// ymin: (output) the descender value from the table (negative!)
+// face: A FreeType TrueType or OpenType font
+// target_pelsize: the pixel size of the font (e.g. 16)
+//
+// Returns true iff a suitable match are found. Otherwise, *ymax and *ymin are
+// untouched.
+// -----------------------------------------------------------------------------
+static bool
+SkFontHost_VDMX_Parse(int* ymax, int* ymin,
+ FT_Face face, unsigned target_pelsize) {
+ FT_Error error;
+
+ // Request the length of the VDMX table (if any)
+ FT_ULong vdmx_length = 0;
+ error = FT_Load_Sfnt_Table(face, FT_MAKE_TAG('V', 'D', 'M', 'X'),
+ 0, NULL, &vdmx_length);
+
+ if (error || vdmx_length > 1024 * 1024)
+ return false;
+
+ uint8_t* vdmx = (uint8_t *) malloc(vdmx_length);
+ error = FT_Load_Sfnt_Table(face, FT_MAKE_TAG('V', 'D', 'M', 'X'),
+ 0, vdmx, NULL);
+ if (error) {
+ free(vdmx);
+ return false;
+ }
+
+ bool result = VDMX_Parse(ymax, ymin, vdmx, vdmx_length, target_pelsize);
+ free(vdmx);
+
+ return result;
+}
+
void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
{
if (NULL == mx && NULL == my)
@@ -775,6 +821,10 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, SkP
mx->fBottom = pts[3].fX;
mx->fLeading = pts[4].fX;
mx->fHeight = pts[5].fX;
+
+ // The VDMX metrics only make sense in the horizontal direction
+ // I believe
+ my->fVDMXMetricsValid = false;
}
if (my)
{
@@ -784,6 +834,16 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, SkP
my->fBottom = pts[3].fY;
my->fLeading = pts[4].fY;
my->fHeight = pts[5].fY;
+ my->fVDMXMetricsValid = false;
+
+ // Attempt to parse the VDMX table to get exact metrics
+ unsigned pelsize = fScaleY >> 16;
+ int ymax, ymin;
+ if (SkFontHost_VDMX_Parse(&ymax, &ymin, face, pelsize)) {
+ my->fVDMXMetricsValid = true;
+ my->fVDMXAscent = ymax;
+ my->fVDMXDescent = -ymin;
+ }
}
}
diff --git a/skia/ports/SkFontHost_TrueType_VDMX.cpp b/skia/ports/SkFontHost_TrueType_VDMX.cpp
new file mode 100644
index 0000000..49b4670
--- /dev/null
+++ b/skia/ports/SkFontHost_TrueType_VDMX.cpp
@@ -0,0 +1,189 @@
+/* libs/graphics/ports/SkFontHost_TrueType_VDMX.cpp
+**
+** Copyright 2008, Google Inc.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+// -----------------------------------------------------------------------------
+// VDMX parsing code.
+//
+// VDMX tables are found in some TrueType/OpenType fonts and contain
+// ascender/descender overrides for certain (usually small) sizes. This is
+// needed in order to match font metrics on Windows.
+//
+// Freetype does not parse these tables so we do so here. In the future we
+// might support loading of arbitary fonts. This is not something that one
+// would wish to do, dangerous as it is, so we are careful where we tread.
+// -----------------------------------------------------------------------------
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+// For htons/ntohs
+#include <arpa/inet.h>
+
+// -----------------------------------------------------------------------------
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+// -----------------------------------------------------------------------------
+class Buffer {
+ public:
+ Buffer(const uint8_t* buffer, size_t length)
+ : buffer_(buffer),
+ length_(length),
+ offset_(0) { }
+
+ bool Skip(size_t n_bytes) {
+ if (offset_ + n_bytes > length_)
+ return false;
+ offset_ += n_bytes;
+ return true;
+ }
+
+ bool ReadU8(uint8_t* value) {
+ if (offset_ + 1 > length_)
+ return false;
+ *value = buffer_[offset_];
+ offset_++;
+ return true;
+ }
+
+ bool ReadU16(uint16_t* value) {
+ if (offset_ + 2 > length_)
+ return false;
+ memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+ *value = ntohs(*value);
+ offset_ += 2;
+ return true;
+ }
+
+ bool ReadS16(int16_t* value) {
+ return ReadU16(reinterpret_cast<uint16_t*>(value));
+ }
+
+ size_t offset() const { return offset_; }
+
+ void set_offset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+ const uint8_t *const buffer_;
+ const size_t length_;
+ size_t offset_;
+};
+
+// -----------------------------------------------------------------------------
+// Parse a TrueType VDMX table.
+// ymax: (output) the ascender value from the table
+// ymin: (output) the descender value from the table (negative!)
+// vdmx: the table bytes
+// vdmx_length: length of @vdmx, in bytes
+// target_pelsize: the pixel size of the font (e.g. 16)
+//
+// Returns true iff a suitable match are found. Otherwise, *ymax and *ymin are
+// untouched. size_t must be 32-bits to avoid overflow.
+//
+// See http://www.microsoft.com/opentype/otspec/vdmx.htm
+// -----------------------------------------------------------------------------
+bool
+VDMX_Parse(int* ymax, int* ymin,
+ const uint8_t* vdmx, const size_t vdmx_length,
+ const unsigned target_pelsize) {
+ Buffer buf(vdmx, vdmx_length);
+
+ // We ignore the version. Future tables should be backwards compatible with
+ // this layout.
+ uint16_t numratios;
+ if (!buf.Skip(4) ||
+ !buf.ReadU16(&numratios))
+ return false;
+
+ // Now we have two tables. Firstly we have @numratios Ratio records, then a
+ // matching array of @numratios offsets. We save the offset of the beginning
+ // of this second table.
+ //
+ // Range 6 <= x <= 262146
+ unsigned long offset_table_offset =
+ buf.offset() + 4 /* sizeof struct ratio */ * numratios;
+
+ unsigned desired_ratio = 0xffffffff;
+ // We read 4 bytes per record, so the offset range is
+ // 6 <= x <= 524286
+ for (unsigned i = 0; i < numratios; ++i) {
+ uint8_t xratio, yratio1, yratio2;
+
+ if (!buf.Skip(1) ||
+ !buf.ReadU8(&xratio) ||
+ !buf.ReadU8(&yratio1) ||
+ !buf.ReadU8(&yratio2))
+ return false;
+
+ // This either covers 1:1, or this is the default entry (0, 0, 0)
+ if ((xratio == 1 && yratio1 <= 1 && yratio2 >= 1) ||
+ (xratio == 0 && yratio1 == 0 && yratio2 == 0)) {
+ desired_ratio = i;
+ break;
+ }
+ }
+
+ if (desired_ratio == 0xffffffff) {
+ // no ratio found
+ return false;
+ }
+
+ // Range 10 <= x <= 393216
+ buf.set_offset(offset_table_offset + sizeof(uint16_t) * desired_ratio);
+
+ // Now we read from the offset table to get the offset of another array
+ uint16_t group_offset;
+ if (!buf.ReadU16(&group_offset))
+ return false;
+ // Range 0 <= x <= 65535
+ buf.set_offset(group_offset);
+
+ uint16_t num_records;
+ if (!buf.ReadU16(&num_records) ||
+ !buf.Skip(sizeof(uint16_t)))
+ return false;
+
+ // We read 6 bytes per record, so the offset range is
+ // 4 <= x <= 458749
+ for (unsigned i = 0; i < num_records; ++i) {
+ uint16_t pel_size;
+ if (!buf.ReadU16(&pel_size))
+ return false;
+ // the entries are sorted, so we can abort early if need be
+ if (pel_size > target_pelsize)
+ return false;
+
+ if (pel_size == target_pelsize) {
+ int16_t t_ymax, t_ymin;
+ if (!buf.ReadS16(&t_ymax) ||
+ !buf.ReadS16(&t_ymin))
+ return false;
+ *ymin = t_ymin;
+ *ymax = t_ymax;
+ return true;
+ } else {
+ if (!buf.Skip(2 * sizeof(int16_t)))
+ return false;
+ }
+ }
+
+ return false;
+}