|
1 // 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/typography/otspec/vdmx.htm |
|
9 |
|
10 #define TABLE_NAME "VDMX" |
|
11 |
|
12 #define DROP_THIS_TABLE \ |
|
13 do { \ |
|
14 delete file->vdmx; \ |
|
15 file->vdmx = 0; \ |
|
16 OTS_FAILURE_MSG("Table discarded"); \ |
|
17 } while (0) |
|
18 |
|
19 namespace ots { |
|
20 |
|
21 bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { |
|
22 Buffer table(data, length); |
|
23 file->vdmx = new OpenTypeVDMX; |
|
24 OpenTypeVDMX * const vdmx = file->vdmx; |
|
25 |
|
26 if (!table.ReadU16(&vdmx->version) || |
|
27 !table.ReadU16(&vdmx->num_recs) || |
|
28 !table.ReadU16(&vdmx->num_ratios)) { |
|
29 return OTS_FAILURE_MSG("Failed to read table header"); |
|
30 } |
|
31 |
|
32 if (vdmx->version > 1) { |
|
33 OTS_WARNING("bad version: %u", vdmx->version); |
|
34 DROP_THIS_TABLE; |
|
35 return true; // continue transcoding |
|
36 } |
|
37 |
|
38 vdmx->rat_ranges.reserve(vdmx->num_ratios); |
|
39 for (unsigned i = 0; i < vdmx->num_ratios; ++i) { |
|
40 OpenTypeVDMXRatioRecord rec; |
|
41 |
|
42 if (!table.ReadU8(&rec.charset) || |
|
43 !table.ReadU8(&rec.x_ratio) || |
|
44 !table.ReadU8(&rec.y_start_ratio) || |
|
45 !table.ReadU8(&rec.y_end_ratio)) { |
|
46 return OTS_FAILURE_MSG("Failed to read ratio header %d", i); |
|
47 } |
|
48 |
|
49 if (rec.charset > 1) { |
|
50 OTS_WARNING("bad charset: %u", rec.charset); |
|
51 DROP_THIS_TABLE; |
|
52 return true; |
|
53 } |
|
54 |
|
55 if (rec.y_start_ratio > rec.y_end_ratio) { |
|
56 OTS_WARNING("bad y ratio"); |
|
57 DROP_THIS_TABLE; |
|
58 return true; |
|
59 } |
|
60 |
|
61 // All values set to zero signal the default grouping to use; |
|
62 // if present, this must be the last Ratio group in the table. |
|
63 if ((i < vdmx->num_ratios - 1u) && |
|
64 (rec.x_ratio == 0) && |
|
65 (rec.y_start_ratio == 0) && |
|
66 (rec.y_end_ratio == 0)) { |
|
67 // workaround for fonts which have 2 or more {0, 0, 0} terminators. |
|
68 OTS_WARNING("superfluous terminator found"); |
|
69 DROP_THIS_TABLE; |
|
70 return true; |
|
71 } |
|
72 |
|
73 vdmx->rat_ranges.push_back(rec); |
|
74 } |
|
75 |
|
76 vdmx->offsets.reserve(vdmx->num_ratios); |
|
77 const size_t current_offset = table.offset(); |
|
78 // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k. |
|
79 for (unsigned i = 0; i < vdmx->num_ratios; ++i) { |
|
80 uint16_t offset; |
|
81 if (!table.ReadU16(&offset)) { |
|
82 return OTS_FAILURE_MSG("Failed to read ratio offset %d", i); |
|
83 } |
|
84 if (current_offset + offset >= length) { // thus doesn't overflow. |
|
85 return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i); |
|
86 } |
|
87 |
|
88 vdmx->offsets.push_back(offset); |
|
89 } |
|
90 |
|
91 vdmx->groups.reserve(vdmx->num_recs); |
|
92 for (unsigned i = 0; i < vdmx->num_recs; ++i) { |
|
93 OpenTypeVDMXGroup group; |
|
94 if (!table.ReadU16(&group.recs) || |
|
95 !table.ReadU8(&group.startsz) || |
|
96 !table.ReadU8(&group.endsz)) { |
|
97 return OTS_FAILURE_MSG("Failed to read record header %d", i); |
|
98 } |
|
99 group.entries.reserve(group.recs); |
|
100 for (unsigned j = 0; j < group.recs; ++j) { |
|
101 OpenTypeVDMXVTable vt; |
|
102 if (!table.ReadU16(&vt.y_pel_height) || |
|
103 !table.ReadS16(&vt.y_max) || |
|
104 !table.ReadS16(&vt.y_min)) { |
|
105 return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j); |
|
106 } |
|
107 if (vt.y_max < vt.y_min) { |
|
108 OTS_WARNING("bad y min/max"); |
|
109 DROP_THIS_TABLE; |
|
110 return true; |
|
111 } |
|
112 |
|
113 // This table must appear in sorted order (sorted by yPelHeight), |
|
114 // but need not be continuous. |
|
115 if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) { |
|
116 OTS_WARNING("the table is not sorted"); |
|
117 DROP_THIS_TABLE; |
|
118 return true; |
|
119 } |
|
120 |
|
121 group.entries.push_back(vt); |
|
122 } |
|
123 vdmx->groups.push_back(group); |
|
124 } |
|
125 |
|
126 return true; |
|
127 } |
|
128 |
|
129 bool ots_vdmx_should_serialise(OpenTypeFile *file) { |
|
130 if (!file->glyf) return false; // this table is not for CFF fonts. |
|
131 return file->vdmx != NULL; |
|
132 } |
|
133 |
|
134 bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) { |
|
135 OpenTypeVDMX * const vdmx = file->vdmx; |
|
136 |
|
137 if (!out->WriteU16(vdmx->version) || |
|
138 !out->WriteU16(vdmx->num_recs) || |
|
139 !out->WriteU16(vdmx->num_ratios)) { |
|
140 return OTS_FAILURE_MSG("Failed to write table header"); |
|
141 } |
|
142 |
|
143 for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) { |
|
144 const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i]; |
|
145 if (!out->Write(&rec.charset, 1) || |
|
146 !out->Write(&rec.x_ratio, 1) || |
|
147 !out->Write(&rec.y_start_ratio, 1) || |
|
148 !out->Write(&rec.y_end_ratio, 1)) { |
|
149 return OTS_FAILURE_MSG("Failed to write ratio %d", i); |
|
150 } |
|
151 } |
|
152 |
|
153 for (unsigned i = 0; i < vdmx->offsets.size(); ++i) { |
|
154 if (!out->WriteU16(vdmx->offsets[i])) { |
|
155 return OTS_FAILURE_MSG("Failed to write ratio offset %d", i); |
|
156 } |
|
157 } |
|
158 |
|
159 for (unsigned i = 0; i < vdmx->groups.size(); ++i) { |
|
160 const OpenTypeVDMXGroup& group = vdmx->groups[i]; |
|
161 if (!out->WriteU16(group.recs) || |
|
162 !out->Write(&group.startsz, 1) || |
|
163 !out->Write(&group.endsz, 1)) { |
|
164 return OTS_FAILURE_MSG("Failed to write group %d", i); |
|
165 } |
|
166 for (unsigned j = 0; j < group.entries.size(); ++j) { |
|
167 const OpenTypeVDMXVTable& vt = group.entries[j]; |
|
168 if (!out->WriteU16(vt.y_pel_height) || |
|
169 !out->WriteS16(vt.y_max) || |
|
170 !out->WriteS16(vt.y_min)) { |
|
171 return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j); |
|
172 } |
|
173 } |
|
174 } |
|
175 |
|
176 return true; |
|
177 } |
|
178 |
|
179 void ots_vdmx_free(OpenTypeFile *file) { |
|
180 delete file->vdmx; |
|
181 } |
|
182 |
|
183 } // namespace ots |