gfx/ots/src/cmap.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include "cmap.h"
michael@0 6
michael@0 7 #include <algorithm>
michael@0 8 #include <set>
michael@0 9 #include <utility>
michael@0 10 #include <vector>
michael@0 11
michael@0 12 #include "maxp.h"
michael@0 13 #include "os2.h"
michael@0 14
michael@0 15 // cmap - Character To Glyph Index Mapping Table
michael@0 16 // http://www.microsoft.com/typography/otspec/cmap.htm
michael@0 17
michael@0 18 #define TABLE_NAME "cmap"
michael@0 19
michael@0 20 namespace {
michael@0 21
michael@0 22 struct CMAPSubtableHeader {
michael@0 23 uint16_t platform;
michael@0 24 uint16_t encoding;
michael@0 25 uint32_t offset;
michael@0 26 uint16_t format;
michael@0 27 uint32_t length;
michael@0 28 uint32_t language;
michael@0 29 };
michael@0 30
michael@0 31 struct Subtable314Range {
michael@0 32 uint16_t start_range;
michael@0 33 uint16_t end_range;
michael@0 34 int16_t id_delta;
michael@0 35 uint16_t id_range_offset;
michael@0 36 uint32_t id_range_offset_offset;
michael@0 37 };
michael@0 38
michael@0 39 // The maximum number of groups in format 12, 13 or 14 subtables.
michael@0 40 // Note: 0xFFFF is the maximum number of glyphs in a single font file.
michael@0 41 const unsigned kMaxCMAPGroups = 0xFFFF;
michael@0 42
michael@0 43 // Glyph array size for the Mac Roman (format 0) table.
michael@0 44 const size_t kFormat0ArraySize = 256;
michael@0 45
michael@0 46 // The upper limit of the Unicode code point.
michael@0 47 const uint32_t kUnicodeUpperLimit = 0x10FFFF;
michael@0 48
michael@0 49 // The maximum number of UVS records (See below).
michael@0 50 const uint32_t kMaxCMAPSelectorRecords = 259;
michael@0 51 // The range of UVSes are:
michael@0 52 // 0x180B-0x180D (3 code points)
michael@0 53 // 0xFE00-0xFE0F (16 code points)
michael@0 54 // 0xE0100-0xE01EF (240 code points)
michael@0 55 const uint32_t kMongolianVSStart = 0x180B;
michael@0 56 const uint32_t kMongolianVSEnd = 0x180D;
michael@0 57 const uint32_t kVSStart = 0xFE00;
michael@0 58 const uint32_t kVSEnd = 0xFE0F;
michael@0 59 const uint32_t kIVSStart = 0xE0100;
michael@0 60 const uint32_t kIVSEnd = 0xE01EF;
michael@0 61 const uint32_t kUVSUpperLimit = 0xFFFFFF;
michael@0 62
michael@0 63 // Parses Format 4 tables
michael@0 64 bool ParseFormat4(ots::OpenTypeFile *file, int platform, int encoding,
michael@0 65 const uint8_t *data, size_t length, uint16_t num_glyphs) {
michael@0 66 ots::Buffer subtable(data, length);
michael@0 67
michael@0 68 // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
michael@0 69 // whole thing and recompacting it, we validate it and include it verbatim
michael@0 70 // in the output.
michael@0 71
michael@0 72 if (!file->os2) {
michael@0 73 return OTS_FAILURE_MSG("Required OS/2 table missing");
michael@0 74 }
michael@0 75
michael@0 76 if (!subtable.Skip(4)) {
michael@0 77 return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
michael@0 78 }
michael@0 79 uint16_t language = 0;
michael@0 80 if (!subtable.ReadU16(&language)) {
michael@0 81 return OTS_FAILURE_MSG("Can't read language");
michael@0 82 }
michael@0 83 if (language) {
michael@0 84 // Platform ID 3 (windows) subtables should have language '0'.
michael@0 85 return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
michael@0 86 }
michael@0 87
michael@0 88 uint16_t segcountx2, search_range, entry_selector, range_shift;
michael@0 89 segcountx2 = search_range = entry_selector = range_shift = 0;
michael@0 90 if (!subtable.ReadU16(&segcountx2) ||
michael@0 91 !subtable.ReadU16(&search_range) ||
michael@0 92 !subtable.ReadU16(&entry_selector) ||
michael@0 93 !subtable.ReadU16(&range_shift)) {
michael@0 94 return OTS_FAILURE_MSG("Failed to read subcmap structure");
michael@0 95 }
michael@0 96
michael@0 97 if (segcountx2 & 1 || search_range & 1) {
michael@0 98 return OTS_FAILURE_MSG("Bad subcmap structure");
michael@0 99 }
michael@0 100 const uint16_t segcount = segcountx2 >> 1;
michael@0 101 // There must be at least one segment according the spec.
michael@0 102 if (segcount < 1) {
michael@0 103 return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
michael@0 104 }
michael@0 105
michael@0 106 // log2segcount is the maximal x s.t. 2^x < segcount
michael@0 107 unsigned log2segcount = 0;
michael@0 108 while (1u << (log2segcount + 1) <= segcount) {
michael@0 109 log2segcount++;
michael@0 110 }
michael@0 111
michael@0 112 const uint16_t expected_search_range = 2 * 1u << log2segcount;
michael@0 113 if (expected_search_range != search_range) {
michael@0 114 return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
michael@0 115 }
michael@0 116
michael@0 117 if (entry_selector != log2segcount) {
michael@0 118 return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
michael@0 119 }
michael@0 120
michael@0 121 const uint16_t expected_range_shift = segcountx2 - search_range;
michael@0 122 if (range_shift != expected_range_shift) {
michael@0 123 return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
michael@0 124 }
michael@0 125
michael@0 126 std::vector<Subtable314Range> ranges(segcount);
michael@0 127
michael@0 128 for (unsigned i = 0; i < segcount; ++i) {
michael@0 129 if (!subtable.ReadU16(&ranges[i].end_range)) {
michael@0 130 return OTS_FAILURE_MSG("Failed to read segment %d", i);
michael@0 131 }
michael@0 132 }
michael@0 133
michael@0 134 uint16_t padding;
michael@0 135 if (!subtable.ReadU16(&padding)) {
michael@0 136 return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
michael@0 137 }
michael@0 138 if (padding) {
michael@0 139 return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
michael@0 140 }
michael@0 141
michael@0 142 for (unsigned i = 0; i < segcount; ++i) {
michael@0 143 if (!subtable.ReadU16(&ranges[i].start_range)) {
michael@0 144 return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
michael@0 145 }
michael@0 146 }
michael@0 147 for (unsigned i = 0; i < segcount; ++i) {
michael@0 148 if (!subtable.ReadS16(&ranges[i].id_delta)) {
michael@0 149 return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
michael@0 150 }
michael@0 151 }
michael@0 152 for (unsigned i = 0; i < segcount; ++i) {
michael@0 153 ranges[i].id_range_offset_offset = subtable.offset();
michael@0 154 if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
michael@0 155 return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
michael@0 156 }
michael@0 157
michael@0 158 if (ranges[i].id_range_offset & 1) {
michael@0 159 // Some font generators seem to put 65535 on id_range_offset
michael@0 160 // for 0xFFFF-0xFFFF range.
michael@0 161 // (e.g., many fonts in http://www.princexml.com/fonts/)
michael@0 162 if (i == segcount - 1u) {
michael@0 163 OTS_WARNING("bad id_range_offset");
michael@0 164 ranges[i].id_range_offset = 0;
michael@0 165 // The id_range_offset value in the transcoded font will not change
michael@0 166 // since this table is not actually "transcoded" yet.
michael@0 167 } else {
michael@0 168 return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
michael@0 169 }
michael@0 170 }
michael@0 171 }
michael@0 172
michael@0 173 // ranges must be ascending order, based on the end_code. Ranges may not
michael@0 174 // overlap.
michael@0 175 for (unsigned i = 1; i < segcount; ++i) {
michael@0 176 if ((i == segcount - 1u) &&
michael@0 177 (ranges[i - 1].start_range == 0xffff) &&
michael@0 178 (ranges[i - 1].end_range == 0xffff) &&
michael@0 179 (ranges[i].start_range == 0xffff) &&
michael@0 180 (ranges[i].end_range == 0xffff)) {
michael@0 181 // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
michael@0 182 // We'll accept them as an exception.
michael@0 183 OTS_WARNING("multiple 0xffff terminators found");
michael@0 184 continue;
michael@0 185 }
michael@0 186
michael@0 187 // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
michael@0 188 // unsorted table...
michael@0 189 if (ranges[i].end_range <= ranges[i - 1].end_range) {
michael@0 190 return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
michael@0 191 }
michael@0 192 if (ranges[i].start_range <= ranges[i - 1].end_range) {
michael@0 193 return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
michael@0 194 }
michael@0 195
michael@0 196 // On many fonts, the value of {first, last}_char_index are incorrect.
michael@0 197 // Fix them.
michael@0 198 if (file->os2->first_char_index != 0xFFFF &&
michael@0 199 ranges[i].start_range != 0xFFFF &&
michael@0 200 file->os2->first_char_index > ranges[i].start_range) {
michael@0 201 file->os2->first_char_index = ranges[i].start_range;
michael@0 202 }
michael@0 203 if (file->os2->last_char_index != 0xFFFF &&
michael@0 204 ranges[i].end_range != 0xFFFF &&
michael@0 205 file->os2->last_char_index < ranges[i].end_range) {
michael@0 206 file->os2->last_char_index = ranges[i].end_range;
michael@0 207 }
michael@0 208 }
michael@0 209
michael@0 210 // The last range must end at 0xffff
michael@0 211 if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
michael@0 212 return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
michael@0 213 ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
michael@0 214 }
michael@0 215
michael@0 216 // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
michael@0 217 // each code-point defined in the table and make sure that they are all valid
michael@0 218 // glyphs and that we don't access anything out-of-bounds.
michael@0 219 for (unsigned i = 0; i < segcount; ++i) {
michael@0 220 for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
michael@0 221 const uint16_t code_point = cp;
michael@0 222 if (ranges[i].id_range_offset == 0) {
michael@0 223 // this is explictly allowed to overflow in the spec
michael@0 224 const uint16_t glyph = code_point + ranges[i].id_delta;
michael@0 225 if (glyph >= num_glyphs) {
michael@0 226 return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
michael@0 227 }
michael@0 228 } else {
michael@0 229 const uint16_t range_delta = code_point - ranges[i].start_range;
michael@0 230 // this might seem odd, but it's true. The offset is relative to the
michael@0 231 // location of the offset value itself.
michael@0 232 const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
michael@0 233 ranges[i].id_range_offset +
michael@0 234 range_delta * 2;
michael@0 235 // We need to be able to access a 16-bit value from this offset
michael@0 236 if (glyph_id_offset + 1 >= length) {
michael@0 237 return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
michael@0 238 }
michael@0 239 uint16_t glyph;
michael@0 240 std::memcpy(&glyph, data + glyph_id_offset, 2);
michael@0 241 glyph = ntohs(glyph);
michael@0 242 if (glyph >= num_glyphs) {
michael@0 243 return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
michael@0 244 }
michael@0 245 }
michael@0 246 }
michael@0 247 }
michael@0 248
michael@0 249 // We accept the table.
michael@0 250 // TODO(yusukes): transcode the subtable.
michael@0 251 if (platform == 3 && encoding == 0) {
michael@0 252 file->cmap->subtable_3_0_4_data = data;
michael@0 253 file->cmap->subtable_3_0_4_length = length;
michael@0 254 } else if (platform == 3 && encoding == 1) {
michael@0 255 file->cmap->subtable_3_1_4_data = data;
michael@0 256 file->cmap->subtable_3_1_4_length = length;
michael@0 257 } else if (platform == 0 && encoding == 3) {
michael@0 258 file->cmap->subtable_0_3_4_data = data;
michael@0 259 file->cmap->subtable_0_3_4_length = length;
michael@0 260 } else {
michael@0 261 return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
michael@0 262 }
michael@0 263
michael@0 264 return true;
michael@0 265 }
michael@0 266
michael@0 267 bool Parse31012(ots::OpenTypeFile *file,
michael@0 268 const uint8_t *data, size_t length, uint16_t num_glyphs) {
michael@0 269 ots::Buffer subtable(data, length);
michael@0 270
michael@0 271 // Format 12 tables are simple. We parse these and fully serialise them
michael@0 272 // later.
michael@0 273
michael@0 274 if (!subtable.Skip(8)) {
michael@0 275 return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
michael@0 276 }
michael@0 277 uint32_t language = 0;
michael@0 278 if (!subtable.ReadU32(&language)) {
michael@0 279 return OTS_FAILURE_MSG("can't read format 12 subtable language");
michael@0 280 }
michael@0 281 if (language) {
michael@0 282 return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
michael@0 283 }
michael@0 284
michael@0 285 uint32_t num_groups = 0;
michael@0 286 if (!subtable.ReadU32(&num_groups)) {
michael@0 287 return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
michael@0 288 }
michael@0 289 if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
michael@0 290 return OTS_FAILURE_MSG("bad format 12 subtable group count %d", num_groups);
michael@0 291 }
michael@0 292
michael@0 293 std::vector<ots::OpenTypeCMAPSubtableRange> &groups
michael@0 294 = file->cmap->subtable_3_10_12;
michael@0 295 groups.resize(num_groups);
michael@0 296
michael@0 297 for (unsigned i = 0; i < num_groups; ++i) {
michael@0 298 if (!subtable.ReadU32(&groups[i].start_range) ||
michael@0 299 !subtable.ReadU32(&groups[i].end_range) ||
michael@0 300 !subtable.ReadU32(&groups[i].start_glyph_id)) {
michael@0 301 return OTS_FAILURE_MSG("can't read format 12 subtable group");
michael@0 302 }
michael@0 303
michael@0 304 if (groups[i].start_range > kUnicodeUpperLimit ||
michael@0 305 groups[i].end_range > kUnicodeUpperLimit ||
michael@0 306 groups[i].start_glyph_id > 0xFFFF) {
michael@0 307 return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
michael@0 308 groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
michael@0 309 }
michael@0 310
michael@0 311 // [0xD800, 0xDFFF] are surrogate code points.
michael@0 312 if (groups[i].start_range >= 0xD800 &&
michael@0 313 groups[i].start_range <= 0xDFFF) {
michael@0 314 return OTS_FAILURE_MSG("format 12 subtable out of range group startCharCode (0x%4X)", groups[i].start_range);
michael@0 315 }
michael@0 316 if (groups[i].end_range >= 0xD800 &&
michael@0 317 groups[i].end_range <= 0xDFFF) {
michael@0 318 return OTS_FAILURE_MSG("format 12 subtable out of range group endCharCode (0x%4X)", groups[i].end_range);
michael@0 319 }
michael@0 320 if (groups[i].start_range < 0xD800 &&
michael@0 321 groups[i].end_range > 0xDFFF) {
michael@0 322 return OTS_FAILURE_MSG("bad format 12 subtable group startCharCode (0x%4X) or endCharCode (0x%4X)",
michael@0 323 groups[i].start_range, groups[i].end_range);
michael@0 324 }
michael@0 325
michael@0 326 // We assert that the glyph value is within range. Because of the range
michael@0 327 // limits, above, we don't need to worry about overflow.
michael@0 328 if (groups[i].end_range < groups[i].start_range) {
michael@0 329 return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
michael@0 330 groups[i].end_range, groups[i].start_range);
michael@0 331 }
michael@0 332 if ((groups[i].end_range - groups[i].start_range) +
michael@0 333 groups[i].start_glyph_id > num_glyphs) {
michael@0 334 return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
michael@0 335 }
michael@0 336 }
michael@0 337
michael@0 338 // the groups must be sorted by start code and may not overlap
michael@0 339 for (unsigned i = 1; i < num_groups; ++i) {
michael@0 340 if (groups[i].start_range <= groups[i - 1].start_range) {
michael@0 341 return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
michael@0 342 groups[i].start_range, groups[i-1].start_range);
michael@0 343 }
michael@0 344 if (groups[i].start_range <= groups[i - 1].end_range) {
michael@0 345 return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
michael@0 346 groups[i].start_range, groups[i-1].end_range);
michael@0 347 }
michael@0 348 }
michael@0 349
michael@0 350 return true;
michael@0 351 }
michael@0 352
michael@0 353 bool Parse31013(ots::OpenTypeFile *file,
michael@0 354 const uint8_t *data, size_t length, uint16_t num_glyphs) {
michael@0 355 ots::Buffer subtable(data, length);
michael@0 356
michael@0 357 // Format 13 tables are simple. We parse these and fully serialise them
michael@0 358 // later.
michael@0 359
michael@0 360 if (!subtable.Skip(8)) {
michael@0 361 return OTS_FAILURE_MSG("Bad cmap subtable length");
michael@0 362 }
michael@0 363 uint16_t language = 0;
michael@0 364 if (!subtable.ReadU16(&language)) {
michael@0 365 return OTS_FAILURE_MSG("Can't read cmap subtable language");
michael@0 366 }
michael@0 367 if (language) {
michael@0 368 return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
michael@0 369 }
michael@0 370
michael@0 371 uint32_t num_groups = 0;
michael@0 372 if (!subtable.ReadU32(&num_groups)) {
michael@0 373 return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
michael@0 374 }
michael@0 375
michael@0 376 // We limit the number of groups in the same way as in 3.10.12 tables. See
michael@0 377 // the comment there in
michael@0 378 if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
michael@0 379 return OTS_FAILURE_MSG("Bad number of groups (%d) in a cmap subtable", num_groups);
michael@0 380 }
michael@0 381
michael@0 382 std::vector<ots::OpenTypeCMAPSubtableRange> &groups
michael@0 383 = file->cmap->subtable_3_10_13;
michael@0 384 groups.resize(num_groups);
michael@0 385
michael@0 386 for (unsigned i = 0; i < num_groups; ++i) {
michael@0 387 if (!subtable.ReadU32(&groups[i].start_range) ||
michael@0 388 !subtable.ReadU32(&groups[i].end_range) ||
michael@0 389 !subtable.ReadU32(&groups[i].start_glyph_id)) {
michael@0 390 return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
michael@0 391 }
michael@0 392
michael@0 393 // We conservatively limit all of the values to protect some parsers from
michael@0 394 // overflows
michael@0 395 if (groups[i].start_range > kUnicodeUpperLimit ||
michael@0 396 groups[i].end_range > kUnicodeUpperLimit ||
michael@0 397 groups[i].start_glyph_id > 0xFFFF) {
michael@0 398 return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
michael@0 399 }
michael@0 400
michael@0 401 if (groups[i].start_glyph_id >= num_glyphs) {
michael@0 402 return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
michael@0 403 }
michael@0 404 }
michael@0 405
michael@0 406 // the groups must be sorted by start code and may not overlap
michael@0 407 for (unsigned i = 1; i < num_groups; ++i) {
michael@0 408 if (groups[i].start_range <= groups[i - 1].start_range) {
michael@0 409 return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
michael@0 410 }
michael@0 411 if (groups[i].start_range <= groups[i - 1].end_range) {
michael@0 412 return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
michael@0 413 }
michael@0 414 }
michael@0 415
michael@0 416 return true;
michael@0 417 }
michael@0 418
michael@0 419 bool Parse0514(ots::OpenTypeFile *file,
michael@0 420 const uint8_t *data, size_t length, uint16_t num_glyphs) {
michael@0 421 // Unicode Variation Selector table
michael@0 422 ots::Buffer subtable(data, length);
michael@0 423
michael@0 424 // Format 14 tables are simple. We parse these and fully serialise them
michael@0 425 // later.
michael@0 426
michael@0 427 // Skip format (USHORT) and length (ULONG)
michael@0 428 if (!subtable.Skip(6)) {
michael@0 429 return OTS_FAILURE_MSG("Can't read start of cmap subtable");
michael@0 430 }
michael@0 431
michael@0 432 uint32_t num_records = 0;
michael@0 433 if (!subtable.ReadU32(&num_records)) {
michael@0 434 return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
michael@0 435 }
michael@0 436 if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
michael@0 437 return OTS_FAILURE_MSG("Bad number of records (%d) in cmap subtable", num_records);
michael@0 438 }
michael@0 439
michael@0 440 std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
michael@0 441 = file->cmap->subtable_0_5_14;
michael@0 442 records.resize(num_records);
michael@0 443
michael@0 444 for (unsigned i = 0; i < num_records; ++i) {
michael@0 445 if (!subtable.ReadU24(&records[i].var_selector) ||
michael@0 446 !subtable.ReadU32(&records[i].default_offset) ||
michael@0 447 !subtable.ReadU32(&records[i].non_default_offset)) {
michael@0 448 return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
michael@0 449 }
michael@0 450 // Checks the value of variation selector
michael@0 451 if (!((records[i].var_selector >= kMongolianVSStart &&
michael@0 452 records[i].var_selector <= kMongolianVSEnd) ||
michael@0 453 (records[i].var_selector >= kVSStart &&
michael@0 454 records[i].var_selector <= kVSEnd) ||
michael@0 455 (records[i].var_selector >= kIVSStart &&
michael@0 456 records[i].var_selector <= kIVSEnd))) {
michael@0 457 return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
michael@0 458 }
michael@0 459 if (i > 0 &&
michael@0 460 records[i-1].var_selector >= records[i].var_selector) {
michael@0 461 return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
michael@0 462 }
michael@0 463
michael@0 464 // Checks offsets
michael@0 465 if (!records[i].default_offset && !records[i].non_default_offset) {
michael@0 466 return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
michael@0 467 }
michael@0 468 if (records[i].default_offset &&
michael@0 469 records[i].default_offset >= length) {
michael@0 470 return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
michael@0 471 }
michael@0 472 if (records[i].non_default_offset &&
michael@0 473 records[i].non_default_offset >= length) {
michael@0 474 return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
michael@0 475 }
michael@0 476 }
michael@0 477
michael@0 478 for (unsigned i = 0; i < num_records; ++i) {
michael@0 479 // Checks default UVS table
michael@0 480 if (records[i].default_offset) {
michael@0 481 subtable.set_offset(records[i].default_offset);
michael@0 482 uint32_t num_ranges = 0;
michael@0 483 if (!subtable.ReadU32(&num_ranges)) {
michael@0 484 return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
michael@0 485 }
michael@0 486 if (!num_ranges || num_ranges > kMaxCMAPGroups) {
michael@0 487 return OTS_FAILURE_MSG("number of ranges too high (%d > %d) in record %d", num_ranges, kMaxCMAPGroups, i);
michael@0 488 }
michael@0 489
michael@0 490 uint32_t last_unicode_value = 0;
michael@0 491 std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
michael@0 492 = records[i].ranges;
michael@0 493 ranges.resize(num_ranges);
michael@0 494
michael@0 495 for (unsigned j = 0; j < num_ranges; ++j) {
michael@0 496 if (!subtable.ReadU24(&ranges[j].unicode_value) ||
michael@0 497 !subtable.ReadU8(&ranges[j].additional_count)) {
michael@0 498 return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
michael@0 499 }
michael@0 500 const uint32_t check_value =
michael@0 501 ranges[j].unicode_value + ranges[j].additional_count;
michael@0 502 if (ranges[j].unicode_value == 0 ||
michael@0 503 ranges[j].unicode_value > kUnicodeUpperLimit ||
michael@0 504 check_value > kUVSUpperLimit ||
michael@0 505 (last_unicode_value &&
michael@0 506 ranges[j].unicode_value <= last_unicode_value)) {
michael@0 507 return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
michael@0 508 }
michael@0 509 last_unicode_value = check_value;
michael@0 510 }
michael@0 511 }
michael@0 512
michael@0 513 // Checks non default UVS table
michael@0 514 if (records[i].non_default_offset) {
michael@0 515 subtable.set_offset(records[i].non_default_offset);
michael@0 516 uint32_t num_mappings = 0;
michael@0 517 if (!subtable.ReadU32(&num_mappings)) {
michael@0 518 return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
michael@0 519 }
michael@0 520 if (!num_mappings || num_mappings > kMaxCMAPGroups) {
michael@0 521 return OTS_FAILURE_MSG("Number of mappings too high (%d) in variation selector record %d", num_mappings, i);
michael@0 522 }
michael@0 523
michael@0 524 uint32_t last_unicode_value = 0;
michael@0 525 std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
michael@0 526 = records[i].mappings;
michael@0 527 mappings.resize(num_mappings);
michael@0 528
michael@0 529 for (unsigned j = 0; j < num_mappings; ++j) {
michael@0 530 if (!subtable.ReadU24(&mappings[j].unicode_value) ||
michael@0 531 !subtable.ReadU16(&mappings[j].glyph_id)) {
michael@0 532 return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
michael@0 533 }
michael@0 534 if (mappings[j].glyph_id == 0 ||
michael@0 535 mappings[j].unicode_value == 0 ||
michael@0 536 mappings[j].unicode_value > kUnicodeUpperLimit ||
michael@0 537 (last_unicode_value &&
michael@0 538 mappings[j].unicode_value <= last_unicode_value)) {
michael@0 539 return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
michael@0 540 }
michael@0 541 last_unicode_value = mappings[j].unicode_value;
michael@0 542 }
michael@0 543 }
michael@0 544 }
michael@0 545
michael@0 546 if (subtable.offset() != length) {
michael@0 547 return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
michael@0 548 }
michael@0 549 file->cmap->subtable_0_5_14_length = subtable.offset();
michael@0 550 return true;
michael@0 551 }
michael@0 552
michael@0 553 bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) {
michael@0 554 // Mac Roman table
michael@0 555 ots::Buffer subtable(data, length);
michael@0 556
michael@0 557 if (!subtable.Skip(4)) {
michael@0 558 return OTS_FAILURE_MSG("Bad cmap subtable");
michael@0 559 }
michael@0 560 uint16_t language = 0;
michael@0 561 if (!subtable.ReadU16(&language)) {
michael@0 562 return OTS_FAILURE_MSG("Can't read language in cmap subtable");
michael@0 563 }
michael@0 564 if (language) {
michael@0 565 // simsun.ttf has non-zero language id.
michael@0 566 OTS_WARNING("language id should be zero: %u", language);
michael@0 567 }
michael@0 568
michael@0 569 file->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
michael@0 570 for (size_t i = 0; i < kFormat0ArraySize; ++i) {
michael@0 571 uint8_t glyph_id = 0;
michael@0 572 if (!subtable.ReadU8(&glyph_id)) {
michael@0 573 return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
michael@0 574 }
michael@0 575 file->cmap->subtable_1_0_0.push_back(glyph_id);
michael@0 576 }
michael@0 577
michael@0 578 return true;
michael@0 579 }
michael@0 580
michael@0 581 } // namespace
michael@0 582
michael@0 583 namespace ots {
michael@0 584
michael@0 585 bool ots_cmap_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
michael@0 586 Buffer table(data, length);
michael@0 587 file->cmap = new OpenTypeCMAP;
michael@0 588
michael@0 589 uint16_t version = 0;
michael@0 590 uint16_t num_tables = 0;
michael@0 591 if (!table.ReadU16(&version) ||
michael@0 592 !table.ReadU16(&num_tables)) {
michael@0 593 return OTS_FAILURE_MSG("Can't read structure of cmap");
michael@0 594 }
michael@0 595
michael@0 596 if (version != 0) {
michael@0 597 return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
michael@0 598 }
michael@0 599 if (!num_tables) {
michael@0 600 return OTS_FAILURE_MSG("No subtables in cmap!");
michael@0 601 }
michael@0 602
michael@0 603 std::vector<CMAPSubtableHeader> subtable_headers;
michael@0 604
michael@0 605 // read the subtable headers
michael@0 606 subtable_headers.reserve(num_tables);
michael@0 607 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 608 CMAPSubtableHeader subt;
michael@0 609
michael@0 610 if (!table.ReadU16(&subt.platform) ||
michael@0 611 !table.ReadU16(&subt.encoding) ||
michael@0 612 !table.ReadU32(&subt.offset)) {
michael@0 613 return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
michael@0 614 }
michael@0 615
michael@0 616 subtable_headers.push_back(subt);
michael@0 617 }
michael@0 618
michael@0 619 const size_t data_offset = table.offset();
michael@0 620
michael@0 621 // make sure that all the offsets are valid.
michael@0 622 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 623 if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
michael@0 624 return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
michael@0 625 }
michael@0 626 if (subtable_headers[i].offset < data_offset ||
michael@0 627 subtable_headers[i].offset >= length) {
michael@0 628 return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
michael@0 629 }
michael@0 630 }
michael@0 631
michael@0 632 // the format of the table is the first couple of bytes in the table. The
michael@0 633 // length of the table is stored in a format-specific way.
michael@0 634 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 635 table.set_offset(subtable_headers[i].offset);
michael@0 636 if (!table.ReadU16(&subtable_headers[i].format)) {
michael@0 637 return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
michael@0 638 }
michael@0 639
michael@0 640 uint16_t len = 0;
michael@0 641 uint16_t lang = 0;
michael@0 642 switch (subtable_headers[i].format) {
michael@0 643 case 0:
michael@0 644 case 4:
michael@0 645 if (!table.ReadU16(&len)) {
michael@0 646 return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
michael@0 647 }
michael@0 648 if (!table.ReadU16(&lang)) {
michael@0 649 return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
michael@0 650 }
michael@0 651 subtable_headers[i].length = len;
michael@0 652 subtable_headers[i].language = lang;
michael@0 653 break;
michael@0 654 case 12:
michael@0 655 case 13:
michael@0 656 if (!table.Skip(2)) {
michael@0 657 return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
michael@0 658 }
michael@0 659 if (!table.ReadU32(&subtable_headers[i].length)) {
michael@0 660 return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
michael@0 661 }
michael@0 662 if (!table.ReadU32(&subtable_headers[i].language)) {
michael@0 663 return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
michael@0 664 }
michael@0 665 break;
michael@0 666 case 14:
michael@0 667 if (!table.ReadU32(&subtable_headers[i].length)) {
michael@0 668 return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
michael@0 669 }
michael@0 670 subtable_headers[i].language = 0;
michael@0 671 break;
michael@0 672 default:
michael@0 673 subtable_headers[i].length = 0;
michael@0 674 subtable_headers[i].language = 0;
michael@0 675 break;
michael@0 676 }
michael@0 677 }
michael@0 678
michael@0 679 // check if the table is sorted first by platform ID, then by encoding ID.
michael@0 680 uint32_t last_id = 0;
michael@0 681 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 682 uint32_t current_id
michael@0 683 = (subtable_headers[i].platform << 24)
michael@0 684 + (subtable_headers[i].encoding << 16)
michael@0 685 + subtable_headers[i].language;
michael@0 686 if ((i != 0) && (last_id >= current_id)) {
michael@0 687 return OTS_FAILURE_MSG("subtable %d with platform ID %d, encoding ID %d, language ID %d "
michael@0 688 "following subtable with platform ID %d, encoding ID %d, language ID %d",
michael@0 689 i,
michael@0 690 (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id),
michael@0 691 (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id));
michael@0 692 }
michael@0 693 last_id = current_id;
michael@0 694 }
michael@0 695
michael@0 696 // Now, verify that all the lengths are sane
michael@0 697 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 698 if (!subtable_headers[i].length) continue;
michael@0 699 if (subtable_headers[i].length > 1024 * 1024 * 1024) {
michael@0 700 return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
michael@0 701 }
michael@0 702 // We know that both the offset and length are < 1GB, so the following
michael@0 703 // addition doesn't overflow
michael@0 704 const uint32_t end_byte
michael@0 705 = subtable_headers[i].offset + subtable_headers[i].length;
michael@0 706 if (end_byte > length) {
michael@0 707 return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
michael@0 708 }
michael@0 709 }
michael@0 710
michael@0 711 // check that the cmap subtables are not overlapping.
michael@0 712 std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
michael@0 713 std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
michael@0 714 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 715 const uint32_t end_byte
michael@0 716 = subtable_headers[i].offset + subtable_headers[i].length;
michael@0 717
michael@0 718 if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
michael@0 719 end_byte)).second) {
michael@0 720 // Sometimes Unicode table and MS table share exactly the same data.
michael@0 721 // We'll allow this.
michael@0 722 continue;
michael@0 723 }
michael@0 724 overlap_checker.push_back(
michael@0 725 std::make_pair(subtable_headers[i].offset,
michael@0 726 static_cast<uint8_t>(1) /* start */));
michael@0 727 overlap_checker.push_back(
michael@0 728 std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
michael@0 729 }
michael@0 730 std::sort(overlap_checker.begin(), overlap_checker.end());
michael@0 731 int overlap_count = 0;
michael@0 732 for (unsigned i = 0; i < overlap_checker.size(); ++i) {
michael@0 733 overlap_count += (overlap_checker[i].second ? 1 : -1);
michael@0 734 if (overlap_count > 1) {
michael@0 735 return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
michael@0 736 }
michael@0 737 }
michael@0 738
michael@0 739 // we grab the number of glyphs in the file from the maxp table to make sure
michael@0 740 // that the character map isn't referencing anything beyound this range.
michael@0 741 if (!file->maxp) {
michael@0 742 return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
michael@0 743 }
michael@0 744 const uint16_t num_glyphs = file->maxp->num_glyphs;
michael@0 745
michael@0 746 // We only support a subset of the possible character map tables. Microsoft
michael@0 747 // 'strongly recommends' that everyone supports the Unicode BMP table with
michael@0 748 // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
michael@0 749 // Platform ID Encoding ID Format
michael@0 750 // 0 0 4 (Unicode Default)
michael@0 751 // 0 3 4 (Unicode BMP)
michael@0 752 // 0 3 12 (Unicode UCS-4)
michael@0 753 // 0 5 14 (Unicode Variation Sequences)
michael@0 754 // 1 0 0 (Mac Roman)
michael@0 755 // 3 0 4 (MS Symbol)
michael@0 756 // 3 1 4 (MS Unicode BMP)
michael@0 757 // 3 10 12 (MS Unicode UCS-4)
michael@0 758 // 3 10 13 (MS UCS-4 Fallback mapping)
michael@0 759 //
michael@0 760 // Note:
michael@0 761 // * 0-0-4 table is (usually) written as a 3-1-4 table. If 3-1-4 table
michael@0 762 // also exists, the 0-0-4 table is ignored.
michael@0 763 // * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
michael@0 764 // Some fonts which include 0-5-14 table seems to be required 0-3-4
michael@0 765 // table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
michael@0 766 // * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
michael@0 767 // exists, the 0-3-12 table is ignored.
michael@0 768 //
michael@0 769
michael@0 770 for (unsigned i = 0; i < num_tables; ++i) {
michael@0 771 if (subtable_headers[i].platform == 0) {
michael@0 772 // Unicode platform
michael@0 773
michael@0 774 if ((subtable_headers[i].encoding == 0) &&
michael@0 775 (subtable_headers[i].format == 4)) {
michael@0 776 // parse and output the 0-0-4 table as 3-1-4 table. Sometimes the 0-0-4
michael@0 777 // table actually points to MS symbol data and thus should be parsed as
michael@0 778 // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
michael@0 779 // recovered in ots_cmap_serialise().
michael@0 780 if (!ParseFormat4(file, 3, 1, data + subtable_headers[i].offset,
michael@0 781 subtable_headers[i].length, num_glyphs)) {
michael@0 782 return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
michael@0 783 }
michael@0 784 } else if ((subtable_headers[i].encoding == 3) &&
michael@0 785 (subtable_headers[i].format == 4)) {
michael@0 786 // parse and output the 0-3-4 table as 0-3-4 table.
michael@0 787 if (!ParseFormat4(file, 0, 3, data + subtable_headers[i].offset,
michael@0 788 subtable_headers[i].length, num_glyphs)) {
michael@0 789 return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
michael@0 790 }
michael@0 791 } else if ((subtable_headers[i].encoding == 3) &&
michael@0 792 (subtable_headers[i].format == 12)) {
michael@0 793 // parse and output the 0-3-12 table as 3-10-12 table.
michael@0 794 if (!Parse31012(file, data + subtable_headers[i].offset,
michael@0 795 subtable_headers[i].length, num_glyphs)) {
michael@0 796 return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
michael@0 797 }
michael@0 798 } else if ((subtable_headers[i].encoding == 5) &&
michael@0 799 (subtable_headers[i].format == 14)) {
michael@0 800 if (!Parse0514(file, data + subtable_headers[i].offset,
michael@0 801 subtable_headers[i].length, num_glyphs)) {
michael@0 802 return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
michael@0 803 }
michael@0 804 }
michael@0 805 } else if (subtable_headers[i].platform == 1) {
michael@0 806 // Mac platform
michael@0 807
michael@0 808 if ((subtable_headers[i].encoding == 0) &&
michael@0 809 (subtable_headers[i].format == 0)) {
michael@0 810 // parse and output the 1-0-0 table.
michael@0 811 if (!Parse100(file, data + subtable_headers[i].offset,
michael@0 812 subtable_headers[i].length)) {
michael@0 813 return OTS_FAILURE();
michael@0 814 }
michael@0 815 }
michael@0 816 } else if (subtable_headers[i].platform == 3) {
michael@0 817 // MS platform
michael@0 818
michael@0 819 switch (subtable_headers[i].encoding) {
michael@0 820 case 0:
michael@0 821 case 1:
michael@0 822 if (subtable_headers[i].format == 4) {
michael@0 823 // parse 3-0-4 or 3-1-4 table.
michael@0 824 if (!ParseFormat4(file, subtable_headers[i].platform,
michael@0 825 subtable_headers[i].encoding,
michael@0 826 data + subtable_headers[i].offset,
michael@0 827 subtable_headers[i].length, num_glyphs)) {
michael@0 828 return OTS_FAILURE();
michael@0 829 }
michael@0 830 }
michael@0 831 break;
michael@0 832 case 10:
michael@0 833 if (subtable_headers[i].format == 12) {
michael@0 834 file->cmap->subtable_3_10_12.clear();
michael@0 835 if (!Parse31012(file, data + subtable_headers[i].offset,
michael@0 836 subtable_headers[i].length, num_glyphs)) {
michael@0 837 return OTS_FAILURE();
michael@0 838 }
michael@0 839 } else if (subtable_headers[i].format == 13) {
michael@0 840 file->cmap->subtable_3_10_13.clear();
michael@0 841 if (!Parse31013(file, data + subtable_headers[i].offset,
michael@0 842 subtable_headers[i].length, num_glyphs)) {
michael@0 843 return OTS_FAILURE();
michael@0 844 }
michael@0 845 }
michael@0 846 break;
michael@0 847 }
michael@0 848 }
michael@0 849 }
michael@0 850
michael@0 851 return true;
michael@0 852 }
michael@0 853
michael@0 854 bool ots_cmap_should_serialise(OpenTypeFile *file) {
michael@0 855 return file->cmap != NULL;
michael@0 856 }
michael@0 857
michael@0 858 bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
michael@0 859 const bool have_034 = file->cmap->subtable_0_3_4_data != NULL;
michael@0 860 const bool have_0514 = file->cmap->subtable_0_5_14.size() != 0;
michael@0 861 const bool have_100 = file->cmap->subtable_1_0_0.size() != 0;
michael@0 862 const bool have_304 = file->cmap->subtable_3_0_4_data != NULL;
michael@0 863 // MS Symbol and MS Unicode tables should not co-exist.
michael@0 864 // See the comment above in 0-0-4 parser.
michael@0 865 const bool have_314 = (!have_304) && file->cmap->subtable_3_1_4_data;
michael@0 866 const bool have_31012 = file->cmap->subtable_3_10_12.size() != 0;
michael@0 867 const bool have_31013 = file->cmap->subtable_3_10_13.size() != 0;
michael@0 868 const unsigned num_subtables = static_cast<unsigned>(have_034) +
michael@0 869 static_cast<unsigned>(have_0514) +
michael@0 870 static_cast<unsigned>(have_100) +
michael@0 871 static_cast<unsigned>(have_304) +
michael@0 872 static_cast<unsigned>(have_314) +
michael@0 873 static_cast<unsigned>(have_31012) +
michael@0 874 static_cast<unsigned>(have_31013);
michael@0 875 const off_t table_start = out->Tell();
michael@0 876
michael@0 877 // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
michael@0 878 // (e.g., old fonts for Mac). We don't support them.
michael@0 879 if (!have_304 && !have_314 && !have_034) {
michael@0 880 return OTS_FAILURE();
michael@0 881 }
michael@0 882
michael@0 883 if (!out->WriteU16(0) ||
michael@0 884 !out->WriteU16(num_subtables)) {
michael@0 885 return OTS_FAILURE();
michael@0 886 }
michael@0 887
michael@0 888 const off_t record_offset = out->Tell();
michael@0 889 if (!out->Pad(num_subtables * 8)) {
michael@0 890 return OTS_FAILURE();
michael@0 891 }
michael@0 892
michael@0 893 const off_t offset_034 = out->Tell();
michael@0 894 if (have_034) {
michael@0 895 if (!out->Write(file->cmap->subtable_0_3_4_data,
michael@0 896 file->cmap->subtable_0_3_4_length)) {
michael@0 897 return OTS_FAILURE();
michael@0 898 }
michael@0 899 }
michael@0 900
michael@0 901 const off_t offset_0514 = out->Tell();
michael@0 902 if (have_0514) {
michael@0 903 const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
michael@0 904 = file->cmap->subtable_0_5_14;
michael@0 905 const unsigned num_records = records.size();
michael@0 906 if (!out->WriteU16(14) ||
michael@0 907 !out->WriteU32(file->cmap->subtable_0_5_14_length) ||
michael@0 908 !out->WriteU32(num_records)) {
michael@0 909 return OTS_FAILURE();
michael@0 910 }
michael@0 911 for (unsigned i = 0; i < num_records; ++i) {
michael@0 912 if (!out->WriteU24(records[i].var_selector) ||
michael@0 913 !out->WriteU32(records[i].default_offset) ||
michael@0 914 !out->WriteU32(records[i].non_default_offset)) {
michael@0 915 return OTS_FAILURE();
michael@0 916 }
michael@0 917 }
michael@0 918 for (unsigned i = 0; i < num_records; ++i) {
michael@0 919 if (records[i].default_offset) {
michael@0 920 const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
michael@0 921 = records[i].ranges;
michael@0 922 const unsigned num_ranges = ranges.size();
michael@0 923 if (!out->Seek(records[i].default_offset + offset_0514) ||
michael@0 924 !out->WriteU32(num_ranges)) {
michael@0 925 return OTS_FAILURE();
michael@0 926 }
michael@0 927 for (unsigned j = 0; j < num_ranges; ++j) {
michael@0 928 if (!out->WriteU24(ranges[j].unicode_value) ||
michael@0 929 !out->WriteU8(ranges[j].additional_count)) {
michael@0 930 return OTS_FAILURE();
michael@0 931 }
michael@0 932 }
michael@0 933 }
michael@0 934 if (records[i].non_default_offset) {
michael@0 935 const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
michael@0 936 = records[i].mappings;
michael@0 937 const unsigned num_mappings = mappings.size();
michael@0 938 if (!out->Seek(records[i].non_default_offset + offset_0514) ||
michael@0 939 !out->WriteU32(num_mappings)) {
michael@0 940 return OTS_FAILURE();
michael@0 941 }
michael@0 942 for (unsigned j = 0; j < num_mappings; ++j) {
michael@0 943 if (!out->WriteU24(mappings[j].unicode_value) ||
michael@0 944 !out->WriteU16(mappings[j].glyph_id)) {
michael@0 945 return OTS_FAILURE();
michael@0 946 }
michael@0 947 }
michael@0 948 }
michael@0 949 }
michael@0 950 }
michael@0 951
michael@0 952 const off_t offset_100 = out->Tell();
michael@0 953 if (have_100) {
michael@0 954 if (!out->WriteU16(0) || // format
michael@0 955 !out->WriteU16(6 + kFormat0ArraySize) || // length
michael@0 956 !out->WriteU16(0)) { // language
michael@0 957 return OTS_FAILURE();
michael@0 958 }
michael@0 959 if (!out->Write(&(file->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
michael@0 960 return OTS_FAILURE();
michael@0 961 }
michael@0 962 }
michael@0 963
michael@0 964 const off_t offset_304 = out->Tell();
michael@0 965 if (have_304) {
michael@0 966 if (!out->Write(file->cmap->subtable_3_0_4_data,
michael@0 967 file->cmap->subtable_3_0_4_length)) {
michael@0 968 return OTS_FAILURE();
michael@0 969 }
michael@0 970 }
michael@0 971
michael@0 972 const off_t offset_314 = out->Tell();
michael@0 973 if (have_314) {
michael@0 974 if (!out->Write(file->cmap->subtable_3_1_4_data,
michael@0 975 file->cmap->subtable_3_1_4_length)) {
michael@0 976 return OTS_FAILURE();
michael@0 977 }
michael@0 978 }
michael@0 979
michael@0 980 const off_t offset_31012 = out->Tell();
michael@0 981 if (have_31012) {
michael@0 982 std::vector<OpenTypeCMAPSubtableRange> &groups
michael@0 983 = file->cmap->subtable_3_10_12;
michael@0 984 const unsigned num_groups = groups.size();
michael@0 985 if (!out->WriteU16(12) ||
michael@0 986 !out->WriteU16(0) ||
michael@0 987 !out->WriteU32(num_groups * 12 + 16) ||
michael@0 988 !out->WriteU32(0) ||
michael@0 989 !out->WriteU32(num_groups)) {
michael@0 990 return OTS_FAILURE();
michael@0 991 }
michael@0 992
michael@0 993 for (unsigned i = 0; i < num_groups; ++i) {
michael@0 994 if (!out->WriteU32(groups[i].start_range) ||
michael@0 995 !out->WriteU32(groups[i].end_range) ||
michael@0 996 !out->WriteU32(groups[i].start_glyph_id)) {
michael@0 997 return OTS_FAILURE();
michael@0 998 }
michael@0 999 }
michael@0 1000 }
michael@0 1001
michael@0 1002 const off_t offset_31013 = out->Tell();
michael@0 1003 if (have_31013) {
michael@0 1004 std::vector<OpenTypeCMAPSubtableRange> &groups
michael@0 1005 = file->cmap->subtable_3_10_13;
michael@0 1006 const unsigned num_groups = groups.size();
michael@0 1007 if (!out->WriteU16(13) ||
michael@0 1008 !out->WriteU16(0) ||
michael@0 1009 !out->WriteU32(num_groups * 12 + 14) ||
michael@0 1010 !out->WriteU32(0) ||
michael@0 1011 !out->WriteU32(num_groups)) {
michael@0 1012 return OTS_FAILURE();
michael@0 1013 }
michael@0 1014
michael@0 1015 for (unsigned i = 0; i < num_groups; ++i) {
michael@0 1016 if (!out->WriteU32(groups[i].start_range) ||
michael@0 1017 !out->WriteU32(groups[i].end_range) ||
michael@0 1018 !out->WriteU32(groups[i].start_glyph_id)) {
michael@0 1019 return OTS_FAILURE();
michael@0 1020 }
michael@0 1021 }
michael@0 1022 }
michael@0 1023
michael@0 1024 const off_t table_end = out->Tell();
michael@0 1025 // We might have hanging bytes from the above's checksum which the OTSStream
michael@0 1026 // then merges into the table of offsets.
michael@0 1027 OTSStream::ChecksumState saved_checksum = out->SaveChecksumState();
michael@0 1028 out->ResetChecksum();
michael@0 1029
michael@0 1030 // Now seek back and write the table of offsets
michael@0 1031 if (!out->Seek(record_offset)) {
michael@0 1032 return OTS_FAILURE();
michael@0 1033 }
michael@0 1034
michael@0 1035 if (have_034) {
michael@0 1036 if (!out->WriteU16(0) ||
michael@0 1037 !out->WriteU16(3) ||
michael@0 1038 !out->WriteU32(offset_034 - table_start)) {
michael@0 1039 return OTS_FAILURE();
michael@0 1040 }
michael@0 1041 }
michael@0 1042
michael@0 1043 if (have_0514) {
michael@0 1044 if (!out->WriteU16(0) ||
michael@0 1045 !out->WriteU16(5) ||
michael@0 1046 !out->WriteU32(offset_0514 - table_start)) {
michael@0 1047 return OTS_FAILURE();
michael@0 1048 }
michael@0 1049 }
michael@0 1050
michael@0 1051 if (have_100) {
michael@0 1052 if (!out->WriteU16(1) ||
michael@0 1053 !out->WriteU16(0) ||
michael@0 1054 !out->WriteU32(offset_100 - table_start)) {
michael@0 1055 return OTS_FAILURE();
michael@0 1056 }
michael@0 1057 }
michael@0 1058
michael@0 1059 if (have_304) {
michael@0 1060 if (!out->WriteU16(3) ||
michael@0 1061 !out->WriteU16(0) ||
michael@0 1062 !out->WriteU32(offset_304 - table_start)) {
michael@0 1063 return OTS_FAILURE();
michael@0 1064 }
michael@0 1065 }
michael@0 1066
michael@0 1067 if (have_314) {
michael@0 1068 if (!out->WriteU16(3) ||
michael@0 1069 !out->WriteU16(1) ||
michael@0 1070 !out->WriteU32(offset_314 - table_start)) {
michael@0 1071 return OTS_FAILURE();
michael@0 1072 }
michael@0 1073 }
michael@0 1074
michael@0 1075 if (have_31012) {
michael@0 1076 if (!out->WriteU16(3) ||
michael@0 1077 !out->WriteU16(10) ||
michael@0 1078 !out->WriteU32(offset_31012 - table_start)) {
michael@0 1079 return OTS_FAILURE();
michael@0 1080 }
michael@0 1081 }
michael@0 1082
michael@0 1083 if (have_31013) {
michael@0 1084 if (!out->WriteU16(3) ||
michael@0 1085 !out->WriteU16(10) ||
michael@0 1086 !out->WriteU32(offset_31013 - table_start)) {
michael@0 1087 return OTS_FAILURE();
michael@0 1088 }
michael@0 1089 }
michael@0 1090
michael@0 1091 if (!out->Seek(table_end)) {
michael@0 1092 return OTS_FAILURE();
michael@0 1093 }
michael@0 1094 out->RestoreChecksum(saved_checksum);
michael@0 1095
michael@0 1096 return true;
michael@0 1097 }
michael@0 1098
michael@0 1099 void ots_cmap_free(OpenTypeFile *file) {
michael@0 1100 delete file->cmap;
michael@0 1101 }
michael@0 1102
michael@0 1103 } // namespace ots

mercurial