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 "hdmx.h" |
6 | | #include "head.h" |
7 | | #include "maxp.h" |
8 | | |
9 | | // hdmx - Horizontal Device Metrics |
10 | | // http://www.microsoft.com/typography/otspec/hdmx.htm |
11 | | |
12 | | namespace ots { |
13 | | |
14 | 1.49k | bool OpenTypeHDMX::Parse(const uint8_t *data, size_t length) { |
15 | 1.49k | Buffer table(data, length); |
16 | | |
17 | 1.49k | OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>( |
18 | 1.49k | GetFont()->GetTypedTable(OTS_TAG_MAXP)); |
19 | 1.49k | OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>( |
20 | 1.49k | GetFont()->GetTypedTable(OTS_TAG_HEAD)); |
21 | 1.49k | if (!head || !maxp) { |
22 | 0 | return Error("Missing maxp or head tables in font, needed by hdmx"); |
23 | 0 | } |
24 | | |
25 | 1.49k | if ((head->flags & 0x14) == 0) { |
26 | | // http://www.microsoft.com/typography/otspec/recom.htm#hdmx |
27 | 130 | return Drop("the table should not be present when bit 2 and 4 of the " |
28 | 130 | "head->flags are not set"); |
29 | 130 | } |
30 | | |
31 | 1.36k | int16_t num_recs; |
32 | 1.36k | if (!table.ReadU16(&this->version) || |
33 | 1.35k | !table.ReadS16(&num_recs) || |
34 | 1.31k | !table.ReadS32(&this->size_device_record)) { |
35 | 76 | return Error("Failed to read table header"); |
36 | 76 | } |
37 | 1.29k | if (this->version != 0) { |
38 | 490 | return Drop("Unsupported version: %u", this->version); |
39 | 490 | } |
40 | 800 | if (num_recs <= 0) { |
41 | 181 | return Drop("Bad numRecords: %d", num_recs); |
42 | 181 | } |
43 | 619 | const int32_t actual_size_device_record = maxp->num_glyphs + 2; |
44 | 619 | if (this->size_device_record < actual_size_device_record) { |
45 | 118 | return Drop("Bad sizeDeviceRecord: %d", this->size_device_record); |
46 | 118 | } |
47 | | |
48 | 501 | this->pad_len = this->size_device_record - actual_size_device_record; |
49 | 501 | if (this->pad_len > 3) { |
50 | 63 | return Error("Bad DeviceRecord padding %d", this->pad_len); |
51 | 63 | } |
52 | | |
53 | 438 | uint8_t last_pixel_size = 0; |
54 | 438 | this->records.reserve(num_recs); |
55 | 1.85k | for (int i = 0; i < num_recs; ++i) { |
56 | 1.66k | OpenTypeHDMXDeviceRecord rec; |
57 | | |
58 | 1.66k | if (!table.ReadU8(&rec.pixel_size) || |
59 | 1.61k | !table.ReadU8(&rec.max_width)) { |
60 | 97 | return Error("Failed to read DeviceRecord %d", i); |
61 | 97 | } |
62 | 1.56k | if ((i != 0) && |
63 | 1.16k | (rec.pixel_size <= last_pixel_size)) { |
64 | 64 | return Drop("DeviceRecord's are not sorted"); |
65 | 64 | } |
66 | 1.50k | last_pixel_size = rec.pixel_size; |
67 | | |
68 | 1.50k | rec.widths.reserve(maxp->num_glyphs); |
69 | 142k | for (unsigned j = 0; j < maxp->num_glyphs; ++j) { |
70 | 140k | uint8_t width; |
71 | 140k | if (!table.ReadU8(&width)) { |
72 | 43 | return Error("Failed to read glyph width %d in DeviceRecord %d", j, i); |
73 | 43 | } |
74 | 140k | rec.widths.push_back(width); |
75 | 140k | } |
76 | | |
77 | 1.46k | if ((this->pad_len > 0) && |
78 | 705 | !table.Skip(this->pad_len)) { |
79 | 43 | return Error("DeviceRecord %d should be padded by %d", i, this->pad_len); |
80 | 43 | } |
81 | | |
82 | 1.41k | this->records.push_back(rec); |
83 | 1.41k | } |
84 | | |
85 | 191 | return true; |
86 | 438 | } |
87 | | |
88 | 5.97k | bool OpenTypeHDMX::ShouldSerialize() { |
89 | 5.97k | return Table::ShouldSerialize() && |
90 | | // this table is not for CFF fonts. |
91 | 731 | GetFont()->GetTable(OTS_TAG_GLYF) != NULL; |
92 | 5.97k | } |
93 | | |
94 | 172 | bool OpenTypeHDMX::Serialize(OTSStream *out) { |
95 | 172 | const int16_t num_recs = static_cast<int16_t>(this->records.size()); |
96 | 172 | if (this->records.size() > |
97 | 172 | static_cast<size_t>(std::numeric_limits<int16_t>::max()) || |
98 | 172 | !out->WriteU16(this->version) || |
99 | 172 | !out->WriteS16(num_recs) || |
100 | 172 | !out->WriteS32(this->size_device_record)) { |
101 | 0 | return Error("Failed to write table header"); |
102 | 0 | } |
103 | | |
104 | 884 | for (int16_t i = 0; i < num_recs; ++i) { |
105 | 712 | const OpenTypeHDMXDeviceRecord& rec = this->records[i]; |
106 | 712 | if (!out->Write(&rec.pixel_size, 1) || |
107 | 712 | !out->Write(&rec.max_width, 1) || |
108 | 712 | !out->Write(&rec.widths[0], rec.widths.size())) { |
109 | 0 | return Error("Failed to write DeviceRecord %d", i); |
110 | 0 | } |
111 | 712 | if ((this->pad_len > 0) && |
112 | 423 | !out->Write((const uint8_t *)"\x00\x00\x00", this->pad_len)) { |
113 | 0 | return Error("Failed to write padding of length %d", this->pad_len); |
114 | 0 | } |
115 | 712 | } |
116 | | |
117 | 172 | return true; |
118 | 172 | } |
119 | | |
120 | | } // namespace ots |