/src/mozilla-central/gfx/ots/src/os2.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2009-2017 The OTS 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 <string> |
6 | | |
7 | | #include "os2.h" |
8 | | #include "head.h" |
9 | | |
10 | | // OS/2 - OS/2 and Windows Metrics |
11 | | // http://www.microsoft.com/typography/otspec/os2.htm |
12 | | |
13 | | namespace ots { |
14 | | |
15 | 0 | bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) { |
16 | 0 | Buffer table(data, length); |
17 | 0 |
|
18 | 0 | if (!table.ReadU16(&this->table.version) || |
19 | 0 | !table.ReadS16(&this->table.avg_char_width) || |
20 | 0 | !table.ReadU16(&this->table.weight_class) || |
21 | 0 | !table.ReadU16(&this->table.width_class) || |
22 | 0 | !table.ReadU16(&this->table.type) || |
23 | 0 | !table.ReadS16(&this->table.subscript_x_size) || |
24 | 0 | !table.ReadS16(&this->table.subscript_y_size) || |
25 | 0 | !table.ReadS16(&this->table.subscript_x_offset) || |
26 | 0 | !table.ReadS16(&this->table.subscript_y_offset) || |
27 | 0 | !table.ReadS16(&this->table.superscript_x_size) || |
28 | 0 | !table.ReadS16(&this->table.superscript_y_size) || |
29 | 0 | !table.ReadS16(&this->table.superscript_x_offset) || |
30 | 0 | !table.ReadS16(&this->table.superscript_y_offset) || |
31 | 0 | !table.ReadS16(&this->table.strikeout_size) || |
32 | 0 | !table.ReadS16(&this->table.strikeout_position) || |
33 | 0 | !table.ReadS16(&this->table.family_class)) { |
34 | 0 | return Error("Error reading basic table elements"); |
35 | 0 | } |
36 | 0 | |
37 | 0 | if (this->table.version > 5) { |
38 | 0 | return Error("Unsupported table version: %u", this->table.version); |
39 | 0 | } |
40 | 0 | |
41 | 0 | if (this->table.weight_class < 1) { |
42 | 0 | Warning("Bad usWeightClass: %u, changing it to %d", |
43 | 0 | this->table.weight_class, 1); |
44 | 0 | this->table.weight_class = 1; |
45 | 0 | } else if (this->table.weight_class > 1000) { |
46 | 0 | Warning("Bad usWeightClass: %u, changing it to %d", |
47 | 0 | this->table.weight_class, 1000); |
48 | 0 | this->table.weight_class = 1000; |
49 | 0 | } |
50 | 0 |
|
51 | 0 | if (this->table.width_class < 1) { |
52 | 0 | Warning("Bad usWidthClass: %u, changing it to %d", |
53 | 0 | this->table.width_class, 1); |
54 | 0 | this->table.width_class = 1; |
55 | 0 | } else if (this->table.width_class > 9) { |
56 | 0 | Warning("Bad usWidthClass: %u, changing it to %d", |
57 | 0 | this->table.width_class, 9); |
58 | 0 | this->table.width_class = 9; |
59 | 0 | } |
60 | 0 |
|
61 | 0 | // lowest 3 bits of fsType are exclusive. |
62 | 0 | if (this->table.type & 0x2) { |
63 | 0 | // mask bits 2 & 3. |
64 | 0 | this->table.type &= 0xfff3u; |
65 | 0 | } else if (this->table.type & 0x4) { |
66 | 0 | // mask bits 1 & 3. |
67 | 0 | this->table.type &= 0xfff4u; |
68 | 0 | } else if (this->table.type & 0x8) { |
69 | 0 | // mask bits 1 & 2. |
70 | 0 | this->table.type &= 0xfff9u; |
71 | 0 | } |
72 | 0 |
|
73 | 0 | // mask reserved bits. use only 0..3, 8, 9 bits. |
74 | 0 | this->table.type &= 0x30f; |
75 | 0 |
|
76 | 0 | #define SET_TO_ZERO(a, b) \ |
77 | 0 | if (this->table.b < 0) { \ |
78 | 0 | Warning("Bad " a ": %d, setting it to zero", this->table.b); \ |
79 | 0 | this->table.b = 0; \ |
80 | 0 | } |
81 | 0 |
|
82 | 0 | SET_TO_ZERO("ySubscriptXSize", subscript_x_size); |
83 | 0 | SET_TO_ZERO("ySubscriptYSize", subscript_y_size); |
84 | 0 | SET_TO_ZERO("ySuperscriptXSize", superscript_x_size); |
85 | 0 | SET_TO_ZERO("ySuperscriptYSize", superscript_y_size); |
86 | 0 | SET_TO_ZERO("yStrikeoutSize", strikeout_size); |
87 | 0 | #undef SET_TO_ZERO |
88 | 0 |
|
89 | 0 | static const char* panose_strings[10] = { |
90 | 0 | "bFamilyType", |
91 | 0 | "bSerifStyle", |
92 | 0 | "bWeight", |
93 | 0 | "bProportion", |
94 | 0 | "bContrast", |
95 | 0 | "bStrokeVariation", |
96 | 0 | "bArmStyle", |
97 | 0 | "bLetterform", |
98 | 0 | "bMidline", |
99 | 0 | "bXHeight", |
100 | 0 | }; |
101 | 0 | for (unsigned i = 0; i < 10; ++i) { |
102 | 0 | if (!table.ReadU8(&this->table.panose[i])) { |
103 | 0 | return Error("Failed to read PANOSE %s", panose_strings[i]); |
104 | 0 | } |
105 | 0 | } |
106 | 0 |
|
107 | 0 | if (!table.ReadU32(&this->table.unicode_range_1) || |
108 | 0 | !table.ReadU32(&this->table.unicode_range_2) || |
109 | 0 | !table.ReadU32(&this->table.unicode_range_3) || |
110 | 0 | !table.ReadU32(&this->table.unicode_range_4) || |
111 | 0 | !table.ReadU32(&this->table.vendor_id) || |
112 | 0 | !table.ReadU16(&this->table.selection) || |
113 | 0 | !table.ReadU16(&this->table.first_char_index) || |
114 | 0 | !table.ReadU16(&this->table.last_char_index) || |
115 | 0 | !table.ReadS16(&this->table.typo_ascender) || |
116 | 0 | !table.ReadS16(&this->table.typo_descender) || |
117 | 0 | !table.ReadS16(&this->table.typo_linegap) || |
118 | 0 | !table.ReadU16(&this->table.win_ascent) || |
119 | 0 | !table.ReadU16(&this->table.win_descent)) { |
120 | 0 | return Error("Error reading more basic table fields"); |
121 | 0 | } |
122 | 0 | |
123 | 0 | // If bit 6 is set, then bits 0 and 5 must be clear. |
124 | 0 | if (this->table.selection & 0x40) { |
125 | 0 | this->table.selection &= 0xffdeu; |
126 | 0 | } |
127 | 0 |
|
128 | 0 | // the settings of bits 0 and 1 must be reflected in the macStyle bits |
129 | 0 | // in the 'head' table. |
130 | 0 | OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>( |
131 | 0 | GetFont()->GetTypedTable(OTS_TAG_HEAD)); |
132 | 0 |
|
133 | 0 | if ((this->table.selection & 0x1) && |
134 | 0 | head && !(head->mac_style & 0x2)) { |
135 | 0 | Warning("Adjusting head.macStyle (italic) to match fsSelection"); |
136 | 0 | head->mac_style |= 0x2; |
137 | 0 | } |
138 | 0 | if ((this->table.selection & 0x2) && |
139 | 0 | head && !(head->mac_style & 0x4)) { |
140 | 0 | Warning("Adjusting head.macStyle (underscore) to match fsSelection"); |
141 | 0 | head->mac_style |= 0x4; |
142 | 0 | } |
143 | 0 |
|
144 | 0 | // While bit 6 on implies that bits 0 and 1 of macStyle are clear, |
145 | 0 | // the reverse is not true. |
146 | 0 | if ((this->table.selection & 0x40) && |
147 | 0 | head && (head->mac_style & 0x3)) { |
148 | 0 | Warning("Adjusting head.macStyle (regular) to match fsSelection"); |
149 | 0 | head->mac_style &= 0xfffcu; |
150 | 0 | } |
151 | 0 |
|
152 | 0 | if ((this->table.version < 4) && |
153 | 0 | (this->table.selection & 0x300)) { |
154 | 0 | // bit 8 and 9 must be unset in OS/2 table versions less than 4. |
155 | 0 | Warning("fsSelection bits 8 and 9 must be unset for table version %d", |
156 | 0 | this->table.version); |
157 | 0 | } |
158 | 0 |
|
159 | 0 | // mask reserved bits. use only 0..9 bits. |
160 | 0 | this->table.selection &= 0x3ff; |
161 | 0 |
|
162 | 0 | if (this->table.first_char_index > this->table.last_char_index) { |
163 | 0 | Warning("usFirstCharIndex %d > usLastCharIndex %d", |
164 | 0 | this->table.first_char_index, this->table.last_char_index); |
165 | 0 | this->table.first_char_index = this->table.last_char_index; |
166 | 0 | } |
167 | 0 | if (this->table.typo_linegap < 0) { |
168 | 0 | Warning("Bad sTypoLineGap, setting it to 0: %d", this->table.typo_linegap); |
169 | 0 | this->table.typo_linegap = 0; |
170 | 0 | } |
171 | 0 |
|
172 | 0 | if (this->table.version < 1) { |
173 | 0 | // http://www.microsoft.com/typography/otspec/os2ver0.htm |
174 | 0 | return true; |
175 | 0 | } |
176 | 0 | |
177 | 0 | if (length < offsetof(OS2Data, code_page_range_2)) { |
178 | 0 | Warning("Bad version number, setting it to 0: %u", this->table.version); |
179 | 0 | // Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version |
180 | 0 | // numbers. Fix them. |
181 | 0 | this->table.version = 0; |
182 | 0 | return true; |
183 | 0 | } |
184 | 0 | |
185 | 0 | if (!table.ReadU32(&this->table.code_page_range_1) || |
186 | 0 | !table.ReadU32(&this->table.code_page_range_2)) { |
187 | 0 | return Error("Failed to read ulCodePageRange1 or ulCodePageRange2"); |
188 | 0 | } |
189 | 0 | |
190 | 0 | if (this->table.version < 2) { |
191 | 0 | // http://www.microsoft.com/typography/otspec/os2ver1.htm |
192 | 0 | return true; |
193 | 0 | } |
194 | 0 | |
195 | 0 | if (length < offsetof(OS2Data, max_context)) { |
196 | 0 | Warning("Bad version number, setting it to 1: %u", this->table.version); |
197 | 0 | // some Japanese fonts (e.g., mona.ttf) have weird version number. |
198 | 0 | // fix them. |
199 | 0 | this->table.version = 1; |
200 | 0 | return true; |
201 | 0 | } |
202 | 0 | |
203 | 0 | if (!table.ReadS16(&this->table.x_height) || |
204 | 0 | !table.ReadS16(&this->table.cap_height) || |
205 | 0 | !table.ReadU16(&this->table.default_char) || |
206 | 0 | !table.ReadU16(&this->table.break_char) || |
207 | 0 | !table.ReadU16(&this->table.max_context)) { |
208 | 0 | return Error("Failed to read version 2-specific fields"); |
209 | 0 | } |
210 | 0 | |
211 | 0 | if (this->table.x_height < 0) { |
212 | 0 | Warning("Bad sxHeight settig it to 0: %d", this->table.x_height); |
213 | 0 | this->table.x_height = 0; |
214 | 0 | } |
215 | 0 | if (this->table.cap_height < 0) { |
216 | 0 | Warning("Bad sCapHeight setting it to 0: %d", this->table.cap_height); |
217 | 0 | this->table.cap_height = 0; |
218 | 0 | } |
219 | 0 |
|
220 | 0 | if (this->table.version < 5) { |
221 | 0 | // http://www.microsoft.com/typography/otspec/os2ver4.htm |
222 | 0 | return true; |
223 | 0 | } |
224 | 0 | |
225 | 0 | if (!table.ReadU16(&this->table.lower_optical_pointsize) || |
226 | 0 | !table.ReadU16(&this->table.upper_optical_pointsize)) { |
227 | 0 | return Error("Failed to read version 5-specific fields"); |
228 | 0 | } |
229 | 0 | |
230 | 0 | if (this->table.lower_optical_pointsize > 0xFFFE) { |
231 | 0 | Warning("usLowerOpticalPointSize is bigger than 0xFFFE: %d", |
232 | 0 | this->table.lower_optical_pointsize); |
233 | 0 | this->table.lower_optical_pointsize = 0xFFFE; |
234 | 0 | } |
235 | 0 |
|
236 | 0 | if (this->table.upper_optical_pointsize < 2) { |
237 | 0 | Warning("usUpperOpticalPointSize is lower than 2: %d", |
238 | 0 | this->table.upper_optical_pointsize); |
239 | 0 | this->table.upper_optical_pointsize = 2; |
240 | 0 | } |
241 | 0 |
|
242 | 0 | return true; |
243 | 0 | } |
244 | | |
245 | 0 | bool OpenTypeOS2::Serialize(OTSStream *out) { |
246 | 0 | if (!out->WriteU16(this->table.version) || |
247 | 0 | !out->WriteS16(this->table.avg_char_width) || |
248 | 0 | !out->WriteU16(this->table.weight_class) || |
249 | 0 | !out->WriteU16(this->table.width_class) || |
250 | 0 | !out->WriteU16(this->table.type) || |
251 | 0 | !out->WriteS16(this->table.subscript_x_size) || |
252 | 0 | !out->WriteS16(this->table.subscript_y_size) || |
253 | 0 | !out->WriteS16(this->table.subscript_x_offset) || |
254 | 0 | !out->WriteS16(this->table.subscript_y_offset) || |
255 | 0 | !out->WriteS16(this->table.superscript_x_size) || |
256 | 0 | !out->WriteS16(this->table.superscript_y_size) || |
257 | 0 | !out->WriteS16(this->table.superscript_x_offset) || |
258 | 0 | !out->WriteS16(this->table.superscript_y_offset) || |
259 | 0 | !out->WriteS16(this->table.strikeout_size) || |
260 | 0 | !out->WriteS16(this->table.strikeout_position) || |
261 | 0 | !out->WriteS16(this->table.family_class)) { |
262 | 0 | return Error("Failed to write basic table data"); |
263 | 0 | } |
264 | 0 | |
265 | 0 | for (unsigned i = 0; i < 10; ++i) { |
266 | 0 | if (!out->Write(&this->table.panose[i], 1)) { |
267 | 0 | return Error("Failed to write PANOSE data"); |
268 | 0 | } |
269 | 0 | } |
270 | 0 |
|
271 | 0 | if (!out->WriteU32(this->table.unicode_range_1) || |
272 | 0 | !out->WriteU32(this->table.unicode_range_2) || |
273 | 0 | !out->WriteU32(this->table.unicode_range_3) || |
274 | 0 | !out->WriteU32(this->table.unicode_range_4) || |
275 | 0 | !out->WriteU32(this->table.vendor_id) || |
276 | 0 | !out->WriteU16(this->table.selection) || |
277 | 0 | !out->WriteU16(this->table.first_char_index) || |
278 | 0 | !out->WriteU16(this->table.last_char_index) || |
279 | 0 | !out->WriteS16(this->table.typo_ascender) || |
280 | 0 | !out->WriteS16(this->table.typo_descender) || |
281 | 0 | !out->WriteS16(this->table.typo_linegap) || |
282 | 0 | !out->WriteU16(this->table.win_ascent) || |
283 | 0 | !out->WriteU16(this->table.win_descent)) { |
284 | 0 | return Error("Failed to write version 1-specific fields"); |
285 | 0 | } |
286 | 0 | |
287 | 0 | if (this->table.version < 1) { |
288 | 0 | return true; |
289 | 0 | } |
290 | 0 | |
291 | 0 | if (!out->WriteU32(this->table.code_page_range_1) || |
292 | 0 | !out->WriteU32(this->table.code_page_range_2)) { |
293 | 0 | return Error("Failed to write codepage ranges"); |
294 | 0 | } |
295 | 0 | |
296 | 0 | if (this->table.version < 2) { |
297 | 0 | return true; |
298 | 0 | } |
299 | 0 | |
300 | 0 | if (!out->WriteS16(this->table.x_height) || |
301 | 0 | !out->WriteS16(this->table.cap_height) || |
302 | 0 | !out->WriteU16(this->table.default_char) || |
303 | 0 | !out->WriteU16(this->table.break_char) || |
304 | 0 | !out->WriteU16(this->table.max_context)) { |
305 | 0 | return Error("Failed to write version 2-specific fields"); |
306 | 0 | } |
307 | 0 | |
308 | 0 | if (this->table.version < 5) { |
309 | 0 | return true; |
310 | 0 | } |
311 | 0 | |
312 | 0 | if (!out->WriteU16(this->table.lower_optical_pointsize) || |
313 | 0 | !out->WriteU16(this->table.upper_optical_pointsize)) { |
314 | 0 | return Error("Failed to write version 5-specific fields"); |
315 | 0 | } |
316 | 0 | |
317 | 0 | return true; |
318 | 0 | } |
319 | | |
320 | | } // namespace ots |