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