gfx/ots/src/glyf.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/ots/src/glyf.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,309 @@
     1.4 +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "glyf.h"
     1.9 +
    1.10 +#include <algorithm>
    1.11 +#include <limits>
    1.12 +
    1.13 +#include "head.h"
    1.14 +#include "loca.h"
    1.15 +#include "maxp.h"
    1.16 +
    1.17 +// glyf - Glyph Data
    1.18 +// http://www.microsoft.com/typography/otspec/glyf.htm
    1.19 +
    1.20 +#define TABLE_NAME "glyf"
    1.21 +
    1.22 +namespace {
    1.23 +
    1.24 +bool ParseFlagsForSimpleGlyph(ots::OpenTypeFile *file,
    1.25 +                              ots::Buffer *table,
    1.26 +                              uint32_t gly_length,
    1.27 +                              uint32_t num_flags,
    1.28 +                              uint32_t *flags_count_logical,
    1.29 +                              uint32_t *flags_count_physical,
    1.30 +                              uint32_t *xy_coordinates_length) {
    1.31 +  uint8_t flag = 0;
    1.32 +  if (!table->ReadU8(&flag)) {
    1.33 +    return OTS_FAILURE_MSG("Can't read flag");
    1.34 +  }
    1.35 +
    1.36 +  uint32_t delta = 0;
    1.37 +  if (flag & (1u << 1)) {  // x-Short
    1.38 +    ++delta;
    1.39 +  } else if (!(flag & (1u << 4))) {
    1.40 +    delta += 2;
    1.41 +  }
    1.42 +
    1.43 +  if (flag & (1u << 2)) {  // y-Short
    1.44 +    ++delta;
    1.45 +  } else if (!(flag & (1u << 5))) {
    1.46 +    delta += 2;
    1.47 +  }
    1.48 +
    1.49 +  if (flag & (1u << 3)) {  // repeat
    1.50 +    if (*flags_count_logical + 1 >= num_flags) {
    1.51 +      return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
    1.52 +    }
    1.53 +    uint8_t repeat = 0;
    1.54 +    if (!table->ReadU8(&repeat)) {
    1.55 +      return OTS_FAILURE_MSG("Can't read repeat value");
    1.56 +    }
    1.57 +    if (repeat == 0) {
    1.58 +      return OTS_FAILURE_MSG("Zero repeat");
    1.59 +    }
    1.60 +    delta += (delta * repeat);
    1.61 +
    1.62 +    *flags_count_logical += repeat;
    1.63 +    if (*flags_count_logical >= num_flags) {
    1.64 +      return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
    1.65 +    }
    1.66 +    ++(*flags_count_physical);
    1.67 +  }
    1.68 +
    1.69 +  if ((flag & (1u << 6)) || (flag & (1u << 7))) {  // reserved flags
    1.70 +    return OTS_FAILURE_MSG("Bad flag value (%d)", flag);
    1.71 +  }
    1.72 +
    1.73 +  *xy_coordinates_length += delta;
    1.74 +  if (gly_length < *xy_coordinates_length) {
    1.75 +    return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
    1.76 +  }
    1.77 +
    1.78 +  return true;
    1.79 +}
    1.80 +
    1.81 +bool ParseSimpleGlyph(ots::OpenTypeFile *file, const uint8_t *data,
    1.82 +                      ots::Buffer *table, int16_t num_contours,
    1.83 +                      uint32_t gly_offset, uint32_t gly_length,
    1.84 +                      uint32_t *new_size) {
    1.85 +  ots::OpenTypeGLYF *glyf = file->glyf;
    1.86 +
    1.87 +  // read the end-points array
    1.88 +  uint16_t num_flags = 0;
    1.89 +  for (int i = 0; i < num_contours; ++i) {
    1.90 +    uint16_t tmp_index = 0;
    1.91 +    if (!table->ReadU16(&tmp_index)) {
    1.92 +      return OTS_FAILURE_MSG("Can't read contour index %d", i);
    1.93 +    }
    1.94 +    if (tmp_index == 0xffffu) {
    1.95 +      return OTS_FAILURE_MSG("Bad contour index %d", i);
    1.96 +    }
    1.97 +    // check if the indices are monotonically increasing
    1.98 +    if (i && (tmp_index + 1 <= num_flags)) {
    1.99 +      return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
   1.100 +    }
   1.101 +    num_flags = tmp_index + 1;
   1.102 +  }
   1.103 +
   1.104 +  uint16_t bytecode_length = 0;
   1.105 +  if (!table->ReadU16(&bytecode_length)) {
   1.106 +    return OTS_FAILURE_MSG("Can't read bytecode length");
   1.107 +  }
   1.108 +  if ((file->maxp->version_1) &&
   1.109 +      (file->maxp->max_size_glyf_instructions < bytecode_length)) {
   1.110 +    return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
   1.111 +  }
   1.112 +
   1.113 +  const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
   1.114 +  if (gly_length < (gly_header_length + bytecode_length)) {
   1.115 +    return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
   1.116 +  }
   1.117 +
   1.118 +  if (ots::g_transcode_hints) {
   1.119 +    glyf->iov.push_back(std::make_pair(
   1.120 +        data + gly_offset,
   1.121 +        static_cast<size_t>(gly_header_length + bytecode_length)));
   1.122 +  } else {
   1.123 +    // enqueue two vectors: the glyph data up to the bytecode length, then
   1.124 +    // a pointer to a static uint16_t 0 to overwrite the length.
   1.125 +    glyf->iov.push_back(std::make_pair(
   1.126 +        data + gly_offset,
   1.127 +        static_cast<size_t>(gly_header_length - 2)));
   1.128 +    glyf->iov.push_back(std::make_pair((const uint8_t*) "\x00\x00",
   1.129 +                                       static_cast<size_t>(2)));
   1.130 +  }
   1.131 +
   1.132 +  if (!table->Skip(bytecode_length)) {
   1.133 +    return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
   1.134 +  }
   1.135 +
   1.136 +  uint32_t flags_count_physical = 0;  // on memory
   1.137 +  uint32_t xy_coordinates_length = 0;
   1.138 +  for (uint32_t flags_count_logical = 0;
   1.139 +       flags_count_logical < num_flags;
   1.140 +       ++flags_count_logical, ++flags_count_physical) {
   1.141 +    if (!ParseFlagsForSimpleGlyph(file,
   1.142 +                                  table,
   1.143 +                                  gly_length,
   1.144 +                                  num_flags,
   1.145 +                                  &flags_count_logical,
   1.146 +                                  &flags_count_physical,
   1.147 +                                  &xy_coordinates_length)) {
   1.148 +      return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
   1.149 +    }
   1.150 +  }
   1.151 +
   1.152 +  if (gly_length < (gly_header_length + bytecode_length +
   1.153 +                    flags_count_physical + xy_coordinates_length)) {
   1.154 +    return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
   1.155 +  }
   1.156 +
   1.157 +  if (gly_length - (gly_header_length + bytecode_length +
   1.158 +                    flags_count_physical + xy_coordinates_length) > 3) {
   1.159 +    // We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
   1.160 +    // zero-padded length.
   1.161 +    return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
   1.162 +  }
   1.163 +
   1.164 +  glyf->iov.push_back(std::make_pair(
   1.165 +      data + gly_offset + gly_header_length + bytecode_length,
   1.166 +      static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
   1.167 +
   1.168 +  *new_size
   1.169 +      = gly_header_length + flags_count_physical + xy_coordinates_length;
   1.170 +  if (ots::g_transcode_hints) {
   1.171 +    *new_size += bytecode_length;
   1.172 +  }
   1.173 +
   1.174 +  return true;
   1.175 +}
   1.176 +
   1.177 +}  // namespace
   1.178 +
   1.179 +namespace ots {
   1.180 +
   1.181 +bool ots_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
   1.182 +  Buffer table(data, length);
   1.183 +
   1.184 +  if (!file->maxp || !file->loca || !file->head) {
   1.185 +    return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
   1.186 +  }
   1.187 +
   1.188 +  OpenTypeGLYF *glyf = new OpenTypeGLYF;
   1.189 +  file->glyf = glyf;
   1.190 +
   1.191 +  const unsigned num_glyphs = file->maxp->num_glyphs;
   1.192 +  std::vector<uint32_t> &offsets = file->loca->offsets;
   1.193 +
   1.194 +  if (offsets.size() != num_glyphs + 1) {
   1.195 +    return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
   1.196 +  }
   1.197 +
   1.198 +  std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
   1.199 +  uint32_t current_offset = 0;
   1.200 +
   1.201 +  for (unsigned i = 0; i < num_glyphs; ++i) {
   1.202 +    const unsigned gly_offset = offsets[i];
   1.203 +    // The LOCA parser checks that these values are monotonic
   1.204 +    const unsigned gly_length = offsets[i + 1] - offsets[i];
   1.205 +    if (!gly_length) {
   1.206 +      // this glyph has no outline (e.g. the space charactor)
   1.207 +      resulting_offsets[i] = current_offset;
   1.208 +      continue;
   1.209 +    }
   1.210 +
   1.211 +    if (gly_offset >= length) {
   1.212 +      return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
   1.213 +    }
   1.214 +    // Since these are unsigned types, the compiler is not allowed to assume
   1.215 +    // that they never overflow.
   1.216 +    if (gly_offset + gly_length < gly_offset) {
   1.217 +      return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
   1.218 +    }
   1.219 +    if (gly_offset + gly_length > length) {
   1.220 +      return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
   1.221 +    }
   1.222 +
   1.223 +    table.set_offset(gly_offset);
   1.224 +    int16_t num_contours, xmin, ymin, xmax, ymax;
   1.225 +    if (!table.ReadS16(&num_contours) ||
   1.226 +        !table.ReadS16(&xmin) ||
   1.227 +        !table.ReadS16(&ymin) ||
   1.228 +        !table.ReadS16(&xmax) ||
   1.229 +        !table.ReadS16(&ymax)) {
   1.230 +      return OTS_FAILURE_MSG("Can't read glyph %d header", i);
   1.231 +    }
   1.232 +
   1.233 +    if (num_contours <= -2) {
   1.234 +      // -2, -3, -4, ... are reserved for future use.
   1.235 +      return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
   1.236 +    }
   1.237 +
   1.238 +    // workaround for fonts in http://www.princexml.com/fonts/
   1.239 +    if ((xmin == 32767) &&
   1.240 +        (xmax == -32767) &&
   1.241 +        (ymin == 32767) &&
   1.242 +        (ymax == -32767)) {
   1.243 +      OTS_WARNING("bad xmin/xmax/ymin/ymax values");
   1.244 +      xmin = xmax = ymin = ymax = 0;
   1.245 +    }
   1.246 +
   1.247 +    if (xmin > xmax || ymin > ymax) {
   1.248 +      return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
   1.249 +    }
   1.250 +
   1.251 +    unsigned new_size = 0;
   1.252 +    if (num_contours >= 0) {
   1.253 +      // this is a simple glyph and might contain bytecode
   1.254 +      if (!ParseSimpleGlyph(file, data, &table,
   1.255 +                            num_contours, gly_offset, gly_length, &new_size)) {
   1.256 +        return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
   1.257 +      }
   1.258 +    } else {
   1.259 +      // it's a composite glyph without any bytecode. Enqueue the whole thing
   1.260 +      glyf->iov.push_back(std::make_pair(data + gly_offset,
   1.261 +                                         static_cast<size_t>(gly_length)));
   1.262 +      new_size = gly_length;
   1.263 +    }
   1.264 +
   1.265 +    resulting_offsets[i] = current_offset;
   1.266 +    // glyphs must be four byte aligned
   1.267 +    // TODO(yusukes): investigate whether this padding is really necessary.
   1.268 +    //                Which part of the spec requires this?
   1.269 +    const unsigned padding = (4 - (new_size & 3)) % 4;
   1.270 +    if (padding) {
   1.271 +      glyf->iov.push_back(std::make_pair(
   1.272 +          reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
   1.273 +          static_cast<size_t>(padding)));
   1.274 +      new_size += padding;
   1.275 +    }
   1.276 +    current_offset += new_size;
   1.277 +  }
   1.278 +  resulting_offsets[num_glyphs] = current_offset;
   1.279 +
   1.280 +  const uint16_t max16 = std::numeric_limits<uint16_t>::max();
   1.281 +  if ((*std::max_element(resulting_offsets.begin(),
   1.282 +                         resulting_offsets.end()) >= (max16 * 2u)) &&
   1.283 +      (file->head->index_to_loc_format != 1)) {
   1.284 +    OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
   1.285 +    file->head->index_to_loc_format = 1;
   1.286 +  }
   1.287 +
   1.288 +  file->loca->offsets = resulting_offsets;
   1.289 +  return true;
   1.290 +}
   1.291 +
   1.292 +bool ots_glyf_should_serialise(OpenTypeFile *file) {
   1.293 +  return file->glyf != NULL;
   1.294 +}
   1.295 +
   1.296 +bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) {
   1.297 +  const OpenTypeGLYF *glyf = file->glyf;
   1.298 +
   1.299 +  for (unsigned i = 0; i < glyf->iov.size(); ++i) {
   1.300 +    if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
   1.301 +      return OTS_FAILURE_MSG("Falied to write glyph %d", i);
   1.302 +    }
   1.303 +  }
   1.304 +
   1.305 +  return true;
   1.306 +}
   1.307 +
   1.308 +void ots_glyf_free(OpenTypeFile *file) {
   1.309 +  delete file->glyf;
   1.310 +}
   1.311 +
   1.312 +}  // namespace ots

mercurial