blob: b400002b95d9d38b053ab21fd70f39d07df9022c [file] [log] [blame]
yusukes@chromium.orgd257d182009-11-04 04:56:32 +00001// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "vdmx.h"
6
7// VDMX - Vertical Device Metrics
8// http://www.microsoft.com/opentype/otspec/vdmx.htm
9
10#define DROP_THIS_TABLE \
11 do { delete file->vdmx; file->vdmx = 0; } while (0)
12
13namespace ots {
14
15bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
16 Buffer table(data, length);
17 file->vdmx = new OpenTypeVDMX;
18 OpenTypeVDMX * const vdmx = file->vdmx;
19
20 if (!table.ReadU16(&vdmx->version) ||
21 !table.ReadU16(&vdmx->num_recs) ||
22 !table.ReadU16(&vdmx->num_ratios)) {
23 return OTS_FAILURE();
24 }
25
26 if (vdmx->version > 1) {
27 OTS_WARNING("bad version: %u", vdmx->version);
28 DROP_THIS_TABLE;
29 return true; // continue transcoding
30 }
31
32 vdmx->rat_ranges.reserve(vdmx->num_ratios);
33 for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
34 OpenTypeVDMXRatioRecord rec;
35
36 if (!table.ReadU8(&rec.charset) ||
37 !table.ReadU8(&rec.x_ratio) ||
38 !table.ReadU8(&rec.y_start_ratio) ||
39 !table.ReadU8(&rec.y_end_ratio)) {
40 return OTS_FAILURE();
41 }
42
43 if (rec.charset > 1) {
44 OTS_WARNING("bad charset: %u", rec.charset);
45 DROP_THIS_TABLE;
46 return true;
47 }
yusukes@chromium.orga4099a32009-11-12 01:43:51 +000048
49 if (rec.y_start_ratio > rec.y_end_ratio) {
yusukes@chromium.orgd257d182009-11-04 04:56:32 +000050 OTS_WARNING("bad y ratio");
51 DROP_THIS_TABLE;
52 return true;
53 }
54
55 // All values set to zero signal the default grouping to use;
56 // if present, this must be the last Ratio group in the table.
57 if ((i < vdmx->num_ratios - 1u) &&
58 (rec.x_ratio == 0) &&
59 (rec.y_start_ratio == 0) &&
60 (rec.y_end_ratio == 0)) {
61 // workaround for fonts which have 2 or more {0, 0, 0} terminators.
62 OTS_WARNING("superfluous terminator found");
63 DROP_THIS_TABLE;
yusukes@chromium.orga4099a32009-11-12 01:43:51 +000064 return true;
yusukes@chromium.orgd257d182009-11-04 04:56:32 +000065 }
66
67 vdmx->rat_ranges.push_back(rec);
68 }
69
70 vdmx->offsets.reserve(vdmx->num_ratios);
71 const size_t current_offset = table.offset();
72 // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
73 for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
74 uint16_t offset;
75 if (!table.ReadU16(&offset)) {
76 return OTS_FAILURE();
77 }
78 if (current_offset + offset >= length) { // thus doesn't overflow.
79 return OTS_FAILURE();
80 }
81
82 vdmx->offsets.push_back(offset);
83 }
84
85 vdmx->groups.reserve(vdmx->num_recs);
86 for (unsigned i = 0; i < vdmx->num_recs; ++i) {
87 OpenTypeVDMXGroup group;
88 if (!table.ReadU16(&group.recs) ||
89 !table.ReadU8(&group.startsz) ||
90 !table.ReadU8(&group.endsz)) {
91 return OTS_FAILURE();
92 }
93 group.entries.reserve(group.recs);
94 for (unsigned j = 0; j < group.recs; ++j) {
95 OpenTypeVDMXVTable vt;
96 if (!table.ReadU16(&vt.y_pel_height) ||
97 !table.ReadS16(&vt.y_max) ||
98 !table.ReadS16(&vt.y_min)) {
99 return OTS_FAILURE();
100 }
101 if (vt.y_max < vt.y_min) {
102 OTS_WARNING("bad y min/max");
103 DROP_THIS_TABLE;
104 return true;
105 }
106
107 // This table must appear in sorted order (sorted by yPelHeight),
108 // but need not be continuous.
109 if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
110 OTS_WARNING("the table is not sorted");
111 DROP_THIS_TABLE;
112 return true;
113 }
114
115 group.entries.push_back(vt);
116 }
117 vdmx->groups.push_back(group);
118 }
119
120 return true;
121}
122
123bool ots_vdmx_should_serialise(OpenTypeFile *file) {
124 if (!file->glyf) return false; // this table is not for CFF fonts.
agl@chromium.org2beaf1d2011-01-21 17:19:34 +0000125 return file->vdmx != NULL;
yusukes@chromium.orgd257d182009-11-04 04:56:32 +0000126}
127
128bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
129 OpenTypeVDMX * const vdmx = file->vdmx;
130
131 if (!out->WriteU16(vdmx->version) ||
132 !out->WriteU16(vdmx->num_recs) ||
133 !out->WriteU16(vdmx->num_ratios)) {
134 return OTS_FAILURE();
135 }
136
137 for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
138 const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
139 if (!out->Write(&rec.charset, 1) ||
140 !out->Write(&rec.x_ratio, 1) ||
141 !out->Write(&rec.y_start_ratio, 1) ||
142 !out->Write(&rec.y_end_ratio, 1)) {
143 return OTS_FAILURE();
144 }
145 }
146
147 for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
148 if (!out->WriteU16(vdmx->offsets[i])) {
149 return OTS_FAILURE();
150 }
151 }
152
153 for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
154 const OpenTypeVDMXGroup& group = vdmx->groups[i];
155 if (!out->WriteU16(group.recs) ||
156 !out->Write(&group.startsz, 1) ||
157 !out->Write(&group.endsz, 1)) {
158 return OTS_FAILURE();
159 }
160 for (unsigned j = 0; j < group.entries.size(); ++j) {
161 const OpenTypeVDMXVTable& vt = group.entries[j];
162 if (!out->WriteU16(vt.y_pel_height) ||
163 !out->WriteS16(vt.y_max) ||
164 !out->WriteS16(vt.y_min)) {
165 return OTS_FAILURE();
166 }
167 }
168 }
169
170 return true;
171}
172
173void ots_vdmx_free(OpenTypeFile *file) {
174 delete file->vdmx;
175}
176
177} // namespace ots