vdmx.cc (5403B)
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 "vdmx.h" 6 7 #include <set> 8 9 // VDMX - Vertical Device Metrics 10 // http://www.microsoft.com/typography/otspec/vdmx.htm 11 12 namespace ots { 13 14 #define TABLE_NAME "VDMX" 15 16 bool OpenTypeVDMX::Parse(const uint8_t *data, size_t length) { 17 Buffer table(data, length); 18 ots::Font* font = this->GetFont(); 19 20 if (!table.ReadU16(&this->version) || 21 !table.ReadU16(&this->num_recs) || 22 !table.ReadU16(&this->num_ratios)) { 23 return Drop("Failed to read table header"); 24 } 25 26 if (this->version > 1) { 27 return Drop("Unsupported table version: %u", this->version); 28 } 29 30 this->rat_ranges.reserve(this->num_ratios); 31 for (unsigned i = 0; i < this->num_ratios; ++i) { 32 OpenTypeVDMXRatioRecord rec; 33 34 if (!table.ReadU8(&rec.charset) || 35 !table.ReadU8(&rec.x_ratio) || 36 !table.ReadU8(&rec.y_start_ratio) || 37 !table.ReadU8(&rec.y_end_ratio)) { 38 return Drop("Failed to read RatioRange record %d", i); 39 } 40 41 if (rec.charset > 1) { 42 return Drop("Unsupported character set: %u", rec.charset); 43 } 44 45 if (rec.y_start_ratio > rec.y_end_ratio) { 46 return Drop("Bad y ratio"); 47 } 48 49 // All values set to zero signal the default grouping to use; 50 // if present, this must be the last Ratio group in the table. 51 if ((i < this->num_ratios - 1u) && 52 (rec.x_ratio == 0) && 53 (rec.y_start_ratio == 0) && 54 (rec.y_end_ratio == 0)) { 55 // workaround for fonts which have 2 or more {0, 0, 0} terminators. 56 return Drop("Superfluous terminator found"); 57 } 58 59 this->rat_ranges.push_back(rec); 60 } 61 62 this->offsets.reserve(this->num_ratios); 63 const size_t current_offset = table.offset(); 64 std::set<uint16_t> unique_offsets; 65 // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k. 66 for (unsigned i = 0; i < this->num_ratios; ++i) { 67 uint16_t offset; 68 if (!table.ReadU16(&offset)) { 69 return Drop("Failed to read ratio offset %d", i); 70 } 71 if (current_offset + offset >= length) { // thus doesn't overflow. 72 return Drop("Bad ratio offset %d for ration %d", offset, i); 73 } 74 75 this->offsets.push_back(offset); 76 unique_offsets.insert(offset); 77 } 78 79 // Check that num_recs is sufficient to provide as many VDMXGroup records 80 // as there are unique offsets; if not, update it (we'll return an error 81 // below if they're not actually present). 82 if (unique_offsets.size() > this->num_recs) { 83 OTS_WARNING("increasing num_recs (%u is too small for %u unique offsets)", 84 this->num_recs, unique_offsets.size()); 85 this->num_recs = unique_offsets.size(); 86 } 87 88 this->groups.reserve(this->num_recs); 89 for (unsigned i = 0; i < this->num_recs; ++i) { 90 OpenTypeVDMXGroup group; 91 if (!table.ReadU16(&group.recs) || 92 !table.ReadU8(&group.startsz) || 93 !table.ReadU8(&group.endsz)) { 94 return Drop("Failed to read record header %d", i); 95 } 96 group.entries.reserve(group.recs); 97 for (unsigned j = 0; j < group.recs; ++j) { 98 OpenTypeVDMXVTable vt; 99 if (!table.ReadU16(&vt.y_pel_height) || 100 !table.ReadS16(&vt.y_max) || 101 !table.ReadS16(&vt.y_min)) { 102 return Drop("Failed to read record %d group %d", i, j); 103 } 104 if (vt.y_max < vt.y_min) { 105 return Drop("bad y min/max"); 106 } 107 108 // This table must appear in sorted order (sorted by yPelHeight), 109 // but need not be continuous. 110 if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) { 111 return Drop("The table is not sorted"); 112 } 113 114 group.entries.push_back(vt); 115 } 116 this->groups.push_back(group); 117 } 118 119 return true; 120 } 121 122 bool OpenTypeVDMX::ShouldSerialize() { 123 return Table::ShouldSerialize() && 124 // this table is not for CFF fonts. 125 GetFont()->GetTable(OTS_TAG_GLYF) != NULL; 126 } 127 128 bool OpenTypeVDMX::Serialize(OTSStream *out) { 129 if (!out->WriteU16(this->version) || 130 !out->WriteU16(this->num_recs) || 131 !out->WriteU16(this->num_ratios)) { 132 return Error("Failed to write table header"); 133 } 134 135 for (unsigned i = 0; i < this->rat_ranges.size(); ++i) { 136 const OpenTypeVDMXRatioRecord& rec = this->rat_ranges[i]; 137 if (!out->Write(&rec.charset, 1) || 138 !out->Write(&rec.x_ratio, 1) || 139 !out->Write(&rec.y_start_ratio, 1) || 140 !out->Write(&rec.y_end_ratio, 1)) { 141 return Error("Failed to write RatioRange record %d", i); 142 } 143 } 144 145 for (unsigned i = 0; i < this->offsets.size(); ++i) { 146 if (!out->WriteU16(this->offsets[i])) { 147 return Error("Failed to write ratio offset %d", i); 148 } 149 } 150 151 for (unsigned i = 0; i < this->groups.size(); ++i) { 152 const OpenTypeVDMXGroup& group = this->groups[i]; 153 if (!out->WriteU16(group.recs) || 154 !out->Write(&group.startsz, 1) || 155 !out->Write(&group.endsz, 1)) { 156 return Error("Failed to write group %d", i); 157 } 158 for (unsigned j = 0; j < group.entries.size(); ++j) { 159 const OpenTypeVDMXVTable& vt = group.entries[j]; 160 if (!out->WriteU16(vt.y_pel_height) || 161 !out->WriteS16(vt.y_max) || 162 !out->WriteS16(vt.y_min)) { 163 return Error("Failed to write group %d entry %d", i, j); 164 } 165 } 166 } 167 168 return true; 169 } 170 171 #undef TABLE_NAME 172 173 } // namespace ots