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