dd vhea/vmtx table support.
- Added vhea.{cc,h} and vmtx.{cc,h}.
- Added metrics.{cc,h} to integrate common functions of hhea/hmtx and vhea/vmtx.
- Moved functions in hhea.cc to metrics.cc.
BUG=77386
TEST=http://code.google.com/p/ots/wiki/HowToTestOts (verified with 3500+ font files)
TEST=test/table_dependencies_test.cc
git-svn-id: http://ots.googlecode.com/svn/trunk@61 a4e77c2c-9104-11de-800e-5b313e0d2bf3
diff --git a/ots-common.gypi b/ots-common.gypi
index f023de3..0432b4a 100644
--- a/ots-common.gypi
+++ b/ots-common.gypi
@@ -45,6 +45,8 @@
'src/ltsh.h',
'src/maxp.cc',
'src/maxp.h',
+ 'src/metrics.cc',
+ 'src/metrics.h',
'src/name.cc',
'src/os2.cc',
'src/os2.h',
@@ -56,6 +58,10 @@
'src/prep.h',
'src/vdmx.cc',
'src/vdmx.h',
+ 'src/vhea.cc',
+ 'src/vhea.h',
+ 'src/vmtx.cc',
+ 'src/vmtx.h',
'src/vorg.cc',
'src/vorg.h',
],
diff --git a/src/hhea.cc b/src/hhea.cc
index f9f81b5..0342ba5 100644
--- a/src/hhea.cc
+++ b/src/hhea.cc
@@ -17,69 +17,14 @@
OpenTypeHHEA *hhea = new OpenTypeHHEA;
file->hhea = hhea;
- uint32_t version = 0;
- if (!table.ReadU32(&version)) {
+ if (!table.ReadU32(&hhea->header.version)) {
return OTS_FAILURE();
}
- if (version >> 16 != 1) {
+ if (hhea->header.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) ||
- !table.ReadS16(&hhea->min_rsb) ||
- !table.ReadS16(&hhea->x_max_extent) ||
- !table.ReadS16(&hhea->caret_slope_rise) ||
- !table.ReadS16(&hhea->caret_slope_run) ||
- !table.ReadS16(&hhea->caret_offset)) {
- return OTS_FAILURE();
- }
-
- if (hhea->ascent < 0) {
- OTS_WARNING("bad ascent: %d", hhea->ascent);
- hhea->ascent = 0;
- }
- if (hhea->linegap < 0) {
- OTS_WARNING("bad linegap: %d", hhea->linegap);
- hhea->linegap = 0;
- }
-
- if (!file->head) {
- return OTS_FAILURE();
- }
-
- // if the font is non-slanted, caret_offset should be zero.
- if (!(file->head->mac_style & 2) &&
- (hhea->caret_offset != 0)) {
- OTS_WARNING("bad caret offset: %d", hhea->caret_offset);
- hhea->caret_offset = 0;
- }
-
- // skip the reserved bytes
- if (!table.Skip(8)) {
- return OTS_FAILURE();
- }
-
- int16_t data_format;
- if (!table.ReadS16(&data_format)) {
- return OTS_FAILURE();
- }
- if (data_format) {
- return OTS_FAILURE();
- }
-
- if (!table.ReadU16(&hhea->num_hmetrics)) {
- return OTS_FAILURE();
- }
-
- if (!file->maxp) {
- return OTS_FAILURE();
- }
-
- if (hhea->num_hmetrics > file->maxp->num_glyphs) {
+ if (!ParseMetricsHeader(file, &table, &hhea->header)) {
return OTS_FAILURE();
}
@@ -91,25 +36,9 @@
}
bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
- const OpenTypeHHEA *hhea = file->hhea;
-
- if (!out->WriteU32(0x00010000) ||
- !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) ||
- !out->WriteS16(hhea->caret_offset) ||
- !out->WriteR64(0) || // reserved
- !out->WriteS16(0) || // metric data format
- !out->WriteU16(hhea->num_hmetrics)) {
+ if (!SerialiseMetricsHeader(out, &file->hhea->header)) {
return OTS_FAILURE();
}
-
return true;
}
diff --git a/src/hhea.h b/src/hhea.h
index 99aafde..bdea9aa 100644
--- a/src/hhea.h
+++ b/src/hhea.h
@@ -5,22 +5,13 @@
#ifndef OTS_HHEA_H_
#define OTS_HHEA_H_
+#include "metrics.h"
#include "ots.h"
namespace ots {
struct OpenTypeHHEA {
- 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;
- int16_t caret_slope_run;
- int16_t caret_offset;
- uint16_t num_hmetrics;
+ OpenTypeMetricsHeader header;
};
} // namespace ots
diff --git a/src/hmtx.cc b/src/hmtx.cc
index 3dcaccd..ea7491d 100644
--- a/src/hmtx.cc
+++ b/src/hmtx.cc
@@ -21,58 +21,10 @@
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;
-
- if (num_hmetrics > file->maxp->num_glyphs) {
+ if (!ParseMetricsTable(&table, file->maxp->num_glyphs,
+ &file->hhea->header, &hmtx->metrics)) {
return OTS_FAILURE();
}
- if (!num_hmetrics) {
- return OTS_FAILURE();
- }
- const unsigned num_lsbs = file->maxp->num_glyphs - num_hmetrics;
-
- hmtx->metrics.reserve(num_hmetrics);
- for (unsigned i = 0; i < num_hmetrics; ++i) {
- uint16_t adv = 0;
- 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 (lsb < file->hhea->min_lsb) {
- OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
- lsb = file->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) {
- // 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;
- }
-
- hmtx->lsbs.push_back(lsb);
- }
return true;
}
@@ -82,21 +34,9 @@
}
bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
- const OpenTypeHMTX *hmtx = file->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();
- }
+ if (!SerialiseMetricsTable(out, &file->hmtx->metrics)) {
+ return OTS_FAILURE();
}
-
- for (unsigned i = 0; i < hmtx->lsbs.size(); ++i) {
- if (!out->WriteS16(hmtx->lsbs[i])) {
- return OTS_FAILURE();
- }
- }
-
return true;
}
diff --git a/src/hmtx.h b/src/hmtx.h
index 79a9cb6..435949c 100644
--- a/src/hmtx.h
+++ b/src/hmtx.h
@@ -5,16 +5,13 @@
#ifndef OTS_HMTX_H_
#define OTS_HMTX_H_
-#include <utility> // std::pair
-#include <vector>
-
+#include "metrics.h"
#include "ots.h"
namespace ots {
struct OpenTypeHMTX {
- std::vector<std::pair<uint16_t, int16_t> > metrics;
- std::vector<int16_t> lsbs;
+ OpenTypeMetricsTable metrics;
};
} // namespace ots
diff --git a/src/metrics.cc b/src/metrics.cc
new file mode 100644
index 0000000..2e9190f
--- /dev/null
+++ b/src/metrics.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// OpenType horizontal and vertical common header format
+// http://www.microsoft.com/opentype/otspec/hhea.htm
+// http://www.microsoft.com/opentype/otspec/vhea.htm
+
+namespace ots {
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+ OpenTypeMetricsHeader *header) {
+ if (!table->ReadS16(&header->ascent) ||
+ !table->ReadS16(&header->descent) ||
+ !table->ReadS16(&header->linegap) ||
+ !table->ReadU16(&header->adv_width_max) ||
+ !table->ReadS16(&header->min_sb1) ||
+ !table->ReadS16(&header->min_sb2) ||
+ !table->ReadS16(&header->max_extent) ||
+ !table->ReadS16(&header->caret_slope_rise) ||
+ !table->ReadS16(&header->caret_slope_run) ||
+ !table->ReadS16(&header->caret_offset)) {
+ return OTS_FAILURE();
+ }
+
+ if (header->ascent < 0) {
+ OTS_WARNING("bad ascent: %d", header->ascent);
+ header->ascent = 0;
+ }
+ if (header->linegap < 0) {
+ OTS_WARNING("bad linegap: %d", header->linegap);
+ header->linegap = 0;
+ }
+
+ if (!file->head) {
+ return OTS_FAILURE();
+ }
+
+ // if the font is non-slanted, caret_offset should be zero.
+ if (!(file->head->mac_style & 2) &&
+ (header->caret_offset != 0)) {
+ OTS_WARNING("bad caret offset: %d", header->caret_offset);
+ header->caret_offset = 0;
+ }
+
+ // skip the reserved bytes
+ if (!table->Skip(8)) {
+ return OTS_FAILURE();
+ }
+
+ int16_t data_format;
+ if (!table->ReadS16(&data_format)) {
+ return OTS_FAILURE();
+ }
+ if (data_format) {
+ return OTS_FAILURE();
+ }
+
+ if (!table->ReadU16(&header->num_metrics)) {
+ return OTS_FAILURE();
+ }
+
+ if (!file->maxp) {
+ return OTS_FAILURE();
+ }
+
+ if (header->num_metrics > file->maxp->num_glyphs) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool SerialiseMetricsHeader(OTSStream *out,
+ const OpenTypeMetricsHeader *header) {
+ if (!out->WriteU32(header->version) ||
+ !out->WriteS16(header->ascent) ||
+ !out->WriteS16(header->descent) ||
+ !out->WriteS16(header->linegap) ||
+ !out->WriteU16(header->adv_width_max) ||
+ !out->WriteS16(header->min_sb1) ||
+ !out->WriteS16(header->min_sb2) ||
+ !out->WriteS16(header->max_extent) ||
+ !out->WriteS16(header->caret_slope_rise) ||
+ !out->WriteS16(header->caret_slope_run) ||
+ !out->WriteS16(header->caret_offset) ||
+ !out->WriteR64(0) || // reserved
+ !out->WriteS16(0) || // metric data format
+ !out->WriteU16(header->num_metrics)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool ParseMetricsTable(Buffer *table,
+ const uint16_t num_glyphs,
+ const OpenTypeMetricsHeader *header,
+ OpenTypeMetricsTable *metrics) {
+ // |num_metrics| 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_metrics = header->num_metrics;
+
+ if (num_metrics > num_glyphs) {
+ return OTS_FAILURE();
+ }
+ if (!num_metrics) {
+ return OTS_FAILURE();
+ }
+ const unsigned num_sbs = num_glyphs - num_metrics;
+
+ metrics->entries.reserve(num_metrics);
+ for (unsigned i = 0; i < num_metrics; ++i) {
+ uint16_t adv = 0;
+ int16_t sb = 0;
+ if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
+ return OTS_FAILURE();
+ }
+
+ // Since so many fonts don't have proper value on |adv| and |sb|,
+ // 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 > header->adv_width_max) {
+ OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max);
+ adv = header->adv_width_max;
+ }
+
+ if (sb < header->min_sb1) {
+ OTS_WARNING("bad sb: %d < %d", sb, header->min_sb1);
+ sb = header->min_sb1;
+ }
+
+ metrics->entries.push_back(std::make_pair(adv, sb));
+ }
+
+ metrics->sbs.reserve(num_sbs);
+ for (unsigned i = 0; i < num_sbs; ++i) {
+ int16_t sb;
+ if (!table->ReadS16(&sb)) {
+ // Some Japanese fonts (e.g., mona.ttf) fail this test.
+ return OTS_FAILURE();
+ }
+
+ if (sb < header->min_sb1) {
+ // 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", sb, header->min_sb1);
+ sb = header->min_sb1;
+ }
+
+ metrics->sbs.push_back(sb);
+ }
+
+ return true;
+}
+
+bool SerialiseMetricsTable(OTSStream *out,
+ const OpenTypeMetricsTable *metrics) {
+ for (unsigned i = 0; i < metrics->entries.size(); ++i) {
+ if (!out->WriteU16(metrics->entries[i].first) ||
+ !out->WriteS16(metrics->entries[i].second)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
+ if (!out->WriteS16(metrics->sbs[i])) {
+ return OTS_FAILURE();
+ }
+ }
+
+ return true;
+}
+
+} // namespace ots
+
diff --git a/src/metrics.h b/src/metrics.h
new file mode 100644
index 0000000..46559e1
--- /dev/null
+++ b/src/metrics.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_METRICS_H_
+#define OTS_METRICS_H_
+
+#include <utility> // std::pair
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMetricsHeader {
+ uint32_t version;
+ int16_t ascent;
+ int16_t descent;
+ int16_t linegap;
+ uint16_t adv_width_max;
+ int16_t min_sb1;
+ int16_t min_sb2;
+ int16_t max_extent;
+ int16_t caret_slope_rise;
+ int16_t caret_slope_run;
+ int16_t caret_offset;
+ uint16_t num_metrics;
+};
+
+struct OpenTypeMetricsTable {
+ std::vector<std::pair<uint16_t, int16_t> > entries;
+ std::vector<int16_t> sbs;
+};
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+ OpenTypeMetricsHeader *header);
+bool SerialiseMetricsHeader(OTSStream *out,
+ const OpenTypeMetricsHeader *header);
+
+bool ParseMetricsTable(Buffer *table,
+ const uint16_t num_glyphs,
+ const OpenTypeMetricsHeader *header,
+ OpenTypeMetricsTable *metrics);
+bool SerialiseMetricsTable(OTSStream *out,
+ const OpenTypeMetricsTable *metrics);
+
+} // namespace ots
+
+#endif // OTS_METRICS_H_
+
diff --git a/src/ots.cc b/src/ots.cc
index 75d9a35..ff0ee76 100644
--- a/src/ots.cc
+++ b/src/ots.cc
@@ -16,17 +16,6 @@
// The OpenType Font File
// http://www.microsoft.com/typography/otspec/cmap.htm
-#define F(name, capname) \
- namespace ots { \
- bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
- bool ots_##name##_should_serialise(OpenTypeFile *f); \
- bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
- void ots_##name##_free(OpenTypeFile *f); \
- }
- // TODO(yusukes): change these function names to follow Chromium coding rule.
-FOR_EACH_TABLE_TYPE
-#undef F
-
namespace {
bool g_debug_output = true;
@@ -150,6 +139,10 @@
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 },
+ { 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 },
// TODO(bashi): Support mort, base, and jstf tables.
{ 0, NULL, NULL, NULL, NULL, false },
};
diff --git a/src/ots.h b/src/ots.h
index 8ba30b3..5acb81f 100644
--- a/src/ots.h
+++ b/src/ots.h
@@ -179,7 +179,9 @@
F(post, POST) \
F(prep, PREP) \
F(vdmx, VDMX) \
- F(vorg, VORG)
+ F(vorg, VORG) \
+ F(vhea, VHEA) \
+ F(vmtx, VMTX)
#define F(name, capname) struct OpenType##capname;
FOR_EACH_TABLE_TYPE
@@ -203,6 +205,15 @@
#undef F
};
+#define F(name, capname) \
+bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+bool ots_##name##_should_serialise(OpenTypeFile *f); \
+bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
+void ots_##name##_free(OpenTypeFile *f);
+// TODO(yusukes): change these function names to follow Chromium coding rule.
+FOR_EACH_TABLE_TYPE
+#undef F
+
} // namespace ots
#endif // OTS_H_
diff --git a/src/vhea.cc b/src/vhea.cc
new file mode 100644
index 0000000..b37b73a
--- /dev/null
+++ b/src/vhea.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vhea.h"
+
+#include "gsub.h"
+#include "head.h"
+#include "maxp.h"
+
+// vhea - Vertical Header Table
+// http://www.microsoft.com/opentype/otspec/vhea.htm
+
+namespace ots {
+
+bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeVHEA *vhea = new OpenTypeVHEA;
+ file->vhea = vhea;
+
+ if (!table.ReadU32(&vhea->header.version)) {
+ return OTS_FAILURE();
+ }
+ if (vhea->header.version != 0x00010000 &&
+ vhea->header.version != 0x00011000) {
+ return OTS_FAILURE();
+ }
+
+ if (!ParseMetricsHeader(file, &table, &vhea->header)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool ots_vhea_should_serialise(OpenTypeFile *file) {
+ // vhea should'nt serialise when vmtx doesn't exist.
+ // Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is
+ // preserved. See http://crbug.com/77386
+ return file->vhea != NULL && file->vmtx != NULL &&
+ ots_gsub_should_serialise(file);
+}
+
+bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
+ if (!SerialiseMetricsHeader(out, &file->vhea->header)) {
+ return OTS_FAILURE();
+ }
+ return true;
+}
+
+void ots_vhea_free(OpenTypeFile *file) {
+ delete file->vhea;
+}
+
+} // namespace ots
+
diff --git a/src/vhea.h b/src/vhea.h
new file mode 100644
index 0000000..f8efde7
--- /dev/null
+++ b/src/vhea.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VHEA_H_
+#define OTS_VHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVHEA {
+ OpenTypeMetricsHeader header;
+};
+
+} // namespace ots
+
+#endif // OTS_VHEA_H_
+
diff --git a/src/vmtx.cc b/src/vmtx.cc
new file mode 100644
index 0000000..04761ab
--- /dev/null
+++ b/src/vmtx.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vmtx.h"
+
+#include "gsub.h"
+#include "maxp.h"
+#include "vhea.h"
+
+// vmtx - Vertical Metrics Table
+// http://www.microsoft.com/opentype/otspec/vmtx.htm
+
+namespace ots {
+
+bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeVMTX *vmtx = new OpenTypeVMTX;
+ file->vmtx = vmtx;
+
+ if (!file->vhea || !file->maxp) {
+ return OTS_FAILURE();
+ }
+
+ if (!ParseMetricsTable(&table, file->maxp->num_glyphs,
+ &file->vhea->header, &vmtx->metrics)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool ots_vmtx_should_serialise(OpenTypeFile *file) {
+ // vmtx should serialise when vhea and GSUB are preserved.
+ // See the comment in ots_vhea_should_serialise().
+ return file->vmtx != NULL && file->vhea != NULL &&
+ ots_gsub_should_serialise(file);
+}
+
+bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+ if (!SerialiseMetricsTable(out, &file->vmtx->metrics)) {
+ return OTS_FAILURE();
+ }
+ return true;
+}
+
+void ots_vmtx_free(OpenTypeFile *file) {
+ delete file->vmtx;
+}
+
+} // namespace ots
+
diff --git a/src/vmtx.h b/src/vmtx.h
new file mode 100644
index 0000000..061dc73
--- /dev/null
+++ b/src/vmtx.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VMTX_H_
+#define OTS_VMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVMTX {
+ OpenTypeMetricsTable metrics;
+};
+
+} // namespace ots
+
+#endif // OTS_VMTX_H_
+
diff --git a/test/SConstruct b/test/SConstruct
index 907dd91..157cec8 100644
--- a/test/SConstruct
+++ b/test/SConstruct
@@ -31,17 +31,21 @@
'../src/loca.cc',
'../src/ltsh.cc',
'../src/maxp.cc',
+ '../src/metrics.cc',
'../src/name.cc',
'../src/os2.cc',
'../src/ots.cc',
'../src/post.cc',
'../src/prep.cc',
'../src/vdmx.cc',
+ '../src/vhea.cc',
+ '../src/vmtx.cc',
'../src/vorg.cc'
])
env.Program('../test/cff_type2_charstring_test.cc', LIBS = ['ots', 'gtest_main'], LIBPATH = '../src')
env.Program('../test/layout_common_table_test.cc', LIBS = ['ots', 'gtest_main'], LIBPATH = '../src')
+env.Program('../test/table_dependencies_test.cc', LIBS = ['ots', 'gtest_main'], LIBPATH = '../src')
env.Program('../test/ot-sanitise.cc', LIBS = ['ots'], LIBPATH='../src')
env.Program('../test/idempotent.cc', LIBS = ['ots', 'freetype', 'z', 'm'], LIBPATH='../src')
env.Program('../test/perf.cc', LIBS = ['ots'], LIBPATH='../src')
diff --git a/test/table_dependencies_test.cc b/test/table_dependencies_test.cc
new file mode 100644
index 0000000..bbaaa30
--- /dev/null
+++ b/test/table_dependencies_test.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+
+#include "gsub.h"
+#include "ots.h"
+#include "ots-memory-stream.h"
+#include "vhea.h"
+#include "vmtx.h"
+
+#define SET_TABLE(name, capname) \
+ do { file.name = new ots::OpenType##capname; } while (0)
+#define SET_LAYOUT_TABLE(name, capname) \
+ do { \
+ if (!file.name) { \
+ SET_TABLE(name, capname); \
+ } \
+ file.name->data = reinterpret_cast<const uint8_t*>(1); \
+ file.name->length = 1; \
+ } while (0)
+#define DROP_TABLE(name) \
+ do { delete file.name; file.name = NULL; } while (0)
+#define DROP_LAYOUT_TABLE(name) \
+ do { file.name->data = NULL; file.name->length = 0; } while (0)
+
+namespace {
+
+class TableDependenciesTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ SET_LAYOUT_TABLE(gsub, GSUB);
+ SET_TABLE(vhea, VHEA);
+ SET_TABLE(vmtx, VMTX);
+ }
+
+ virtual void TearDown() {
+ DROP_TABLE(gsub);
+ DROP_TABLE(vhea);
+ DROP_TABLE(vmtx);
+ }
+ ots::OpenTypeFile file;
+};
+} // namespace
+
+TEST_F(TableDependenciesTest, TestVhea) {
+ EXPECT_TRUE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtx) {
+ EXPECT_TRUE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaVmtx) {
+ DROP_TABLE(vmtx);
+ EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxVhea) {
+ DROP_TABLE(vhea);
+ EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaGsub) {
+ DROP_LAYOUT_TABLE(gsub);
+ EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+ DROP_TABLE(gsub);
+ EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxGsub) {
+ DROP_LAYOUT_TABLE(gsub);
+ EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+ DROP_TABLE(gsub);
+ EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+