agl / otc

OpenType Condom

This URL has Read+Write access

Adam Langley (author)
Thu Apr 09 14:55:48 -0700 2009
otc / src / glyf.cc
100644 136 lines (110 sloc) 4.234 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "otc.h"
#include "loca.h"
#include "glyf.h"
#include "maxp.h"
 
bool
otc_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
  Buffer table(data, length);
 
  // http://www.microsoft.com/typography/otspec/glyf.htm
 
  // The GLYF table is pretty complicated. Thankfully, we can skip most of the
  // complexity. For simple glyphs, we just want to remove the hinting
  // bytecode. For composite glyphs, we can pass it directly since we'll
  // already have removed the hinting code from the individual components.
 
  if (!file->maxp || !file->loca)
    return failure();
 
  OpenTypeGLYF *glyf = new OpenTypeGLYF;
  file->glyf = glyf;
 
  const unsigned num_glyphs = file->maxp->num_glyphs;
  std::vector<uint32_t> &offsets = file->loca->offsets;
 
  if (offsets.size() != num_glyphs + 1)
    return failure();
 
  std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
  uint32_t current_offset = 0;
 
  for (unsigned i = 0; i < num_glyphs; ++i) {
    const unsigned gly_offset = offsets[i];
    // The LOCA parser checks that these values are monotonic
    const unsigned gly_length = offsets[i + 1] - offsets[i];
    if (!gly_length) {
      // this glyph has no outline (e.g. the space charactor)
      resulting_offsets[i] = current_offset;
      continue;
    }
 
    if (gly_offset >= length)
      return failure();
    // Since these are unsigned types, the compiler is not allowed to assume
    // that they never overflow.
    if (gly_offset + gly_length < gly_offset)
      return failure();
    if (gly_offset + gly_length > length)
      return failure();
 
    table.set_offset(gly_offset);
    int16_t num_contours, xmin, ymin, xmax, ymax;
    if (!table.ReadS16(&num_contours) ||
        !table.ReadS16(&xmin) ||
        !table.ReadS16(&ymin) ||
        !table.ReadS16(&xmax) ||
        !table.ReadS16(&ymax)) {
      return failure();
    }
 
    if (xmin > xmax || ymin > ymax)
      return failure();
 
    unsigned size_reduction = 0;
 
    if (num_contours >= 0) {
      // this is a simple glyph and might contain bytecode
 
      // skip the end-points array
      table.Skip(num_contours * 2);
      uint16_t bytecode_length;
      if (!table.ReadU16(&bytecode_length))
        return failure();
 
      // when we remove the bytecode we need to shorten the glyph by this
      // amount.
      size_reduction = bytecode_length;
 
      // enqueue three vectors: the glyph data up to the bytecode length, then
      // a pointer to a static uint16_t 0 to overwrite the length, followed by
      // the rest of the glyph.
      const unsigned gly_header_length = 10 + num_contours * 2 + 2;
      glyf->iov.push_back(std::make_pair(data + gly_offset, gly_header_length - 2));
      glyf->iov.push_back(std::make_pair((const uint8_t*) "\x00\x00", 2));
      if (gly_length < (gly_header_length + bytecode_length))
        return failure();
      glyf->iov.push_back(std::make_pair(data + gly_offset + gly_header_length + bytecode_length,
                                         gly_length - (gly_header_length + bytecode_length)));
    } else {
      // it's a composite glyph without any bytecode. Enqueue the whole thing
      glyf->iov.push_back(std::make_pair(data + gly_offset, gly_length));
    }
 
    resulting_offsets[i] = current_offset;
    if (size_reduction > gly_length)
      return failure();
    unsigned new_size = gly_length - size_reduction;
    if (new_size < 14)
      return failure();
    // glyphs must be four byte aligned
    const unsigned padding = (4 - (new_size & 3)) % 4;
    if (padding) {
      glyf->iov.push_back(std::make_pair((const uint8_t*) "\x00\x00\x00\x00", padding));
      new_size += padding;
    }
    current_offset += new_size;
  }
  resulting_offsets[num_glyphs] = current_offset;
 
  file->loca->offsets = resulting_offsets;
 
  return true;
}
 
bool
otc_glyf_should_serialise(OpenTypeFile *file) {
  return file->glyf;
}
 
bool
otc_glyf_serialise(OTCStream *out, OpenTypeFile *file) {
  const OpenTypeGLYF *glyf = file->glyf;
 
  for (unsigned i = 0; i < glyf->iov.size(); ++i) {
    if (!out->Write(glyf->iov[i].first, glyf->iov[i].second))
      return failure();
  }
 
  return true;
}
 
void
otc_glyf_free(OpenTypeFile *file) {
  delete file->glyf;
}