Coverage Report

Created: 2026-10-17 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/post.cc
Line
Count
Source
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 "post.h"
6
7
#include "maxp.h"
8
9
// post - PostScript
10
// http://www.microsoft.com/typography/otspec/post.htm
11
12
namespace ots {
13
14
89.8k
bool OpenTypePOST::Parse(const uint8_t *data, size_t length) {
15
89.8k
  Buffer table(data, length);
16
17
89.8k
  if (!table.ReadU32(&this->version)) {
18
70
    return Error("Failed to read table version");
19
70
  }
20
21
89.7k
  if (this->version != 0x00010000 &&
22
10.3k
      this->version != 0x00020000 &&
23
5.68k
      this->version != 0x00030000) {
24
    // 0x00025000 is deprecated. We don't accept it.
25
454
    return Error("Unsupported table version 0x%x", this->version);
26
454
  }
27
28
89.2k
  if (!table.ReadU32(&this->italic_angle) ||
29
89.0k
      !table.ReadS16(&this->underline) ||
30
89.0k
      !table.ReadS16(&this->underline_thickness) ||
31
88.9k
      !table.ReadU32(&this->is_fixed_pitch) ||
32
      // We don't care about the memory usage fields. We'll set all these to
33
      // zero when serialising
34
88.9k
      !table.Skip(16)) {
35
401
    return Error("Failed to read table header");
36
401
  }
37
38
88.8k
  if (this->underline_thickness < 0) {
39
10.3k
    this->underline_thickness = 1;
40
10.3k
  }
41
42
88.8k
  if (this->version == 0x00010000 || this->version == 0x00030000) {
43
84.2k
    return true;
44
84.2k
  }
45
46
  // We have a version 2 table with a list of Pascal strings at the end
47
48
4.67k
  uint16_t num_glyphs = 0;
49
4.67k
  if (!table.ReadU16(&num_glyphs)) {
50
44
    return Error("Failed to read numberOfGlyphs");
51
44
  }
52
53
4.62k
  OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>
54
4.62k
    (GetFont()->GetTable(OTS_TAG_MAXP));
55
4.62k
  if (!maxp) {
56
0
    return Error("Missing required maxp table");
57
0
  }
58
59
4.62k
  if (num_glyphs == 0) {
60
2.61k
    if (maxp->num_glyphs > 258) {
61
1
      return Error("Can't have no glyphs in the post table if there are more "
62
1
                   "than 258 glyphs in the font");
63
1
    }
64
    // workaround for fonts in http://www.fontsquirrel.com/fontface
65
    // (e.g., yataghan.ttf).
66
2.61k
    this->version = 0x00010000;
67
2.61k
    return Warning("Table version is 1, but no glyph names are found");
68
2.61k
  }
69
70
2.01k
  if (num_glyphs != maxp->num_glyphs) {
71
    // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
72
61
    return Error("Bad number of glyphs: %d", num_glyphs);
73
61
  }
74
75
1.94k
  this->glyph_name_index.resize(num_glyphs);
76
208k
  for (unsigned i = 0; i < num_glyphs; ++i) {
77
206k
    if (!table.ReadU16(&this->glyph_name_index[i])) {
78
38
      return Error("Failed to read glyph name %d", i);
79
38
    }
80
    // Note: A strict interpretation of the specification requires name indexes
81
    // are less than 32768. This, however, excludes fonts like unifont.ttf
82
    // which cover all of unicode.
83
206k
  }
84
85
  // Now we have an array of Pascal strings. We have to check that they are all
86
  // valid and read them in.
87
1.91k
  const size_t strings_offset = table.offset();
88
1.91k
  const uint8_t *strings = data + strings_offset;
89
1.91k
  const uint8_t *strings_end = data + length;
90
91
216k
  for (;;) {
92
216k
    if (strings == strings_end) break;
93
214k
    const unsigned string_length = *strings;
94
214k
    if (strings + 1 + string_length > strings_end) {
95
38
      return Error("Bad string length %d", string_length);
96
38
    }
97
214k
    if (std::memchr(strings + 1, '\0', string_length)) {
98
60
      return Error("Bad string of length %d", string_length);
99
60
    }
100
214k
    this->names.push_back(
101
214k
        std::string(reinterpret_cast<const char*>(strings + 1), string_length));
102
214k
    strings += 1 + string_length;
103
214k
  }
104
1.81k
  const unsigned num_strings = this->names.size();
105
106
  // check that all the references are within bounds
107
207k
  for (unsigned i = 0; i < num_glyphs; ++i) {
108
205k
    unsigned offset = this->glyph_name_index[i];
109
205k
    if (offset < 258) {
110
40.5k
      continue;
111
40.5k
    }
112
113
165k
    offset -= 258;
114
165k
    if (offset >= num_strings) {
115
43
      return Error("Bad string index %d", offset);
116
43
    }
117
165k
  }
118
119
1.77k
  return true;
120
1.81k
}
121
122
46.6k
bool OpenTypePOST::Serialize(OTSStream *out) {
123
  // OpenType with CFF glyphs must have v3 post table.
124
46.6k
  if (GetFont()->GetTable(OTS_TAG_CFF) && this->version != 0x00030000) {
125
4
    Warning("Only version supported for fonts with CFF table is 0x00030000"
126
4
            " not 0x%x", this->version);
127
4
    this->version = 0x00030000;
128
4
  }
129
130
46.6k
  if (!out->WriteU32(this->version) ||
131
46.6k
      !out->WriteU32(this->italic_angle) ||
132
46.6k
      !out->WriteS16(this->underline) ||
133
46.6k
      !out->WriteS16(this->underline_thickness) ||
134
46.6k
      !out->WriteU32(this->is_fixed_pitch) ||
135
46.6k
      !out->WriteU32(0) ||
136
46.6k
      !out->WriteU32(0) ||
137
46.6k
      !out->WriteU32(0) ||
138
46.6k
      !out->WriteU32(0)) {
139
8
    return Error("Failed to write post header");
140
8
  }
141
142
46.6k
  if (this->version != 0x00020000) {
143
46.3k
    return true;  // v1.0 and v3.0 does not have glyph names.
144
46.3k
  }
145
146
321
  const uint16_t num_indexes =
147
321
      static_cast<uint16_t>(this->glyph_name_index.size());
148
321
  if (num_indexes != this->glyph_name_index.size() ||
149
321
      !out->WriteU16(num_indexes)) {
150
0
    return Error("Failed to write number of indices");
151
0
  }
152
153
87.7k
  for (uint16_t i = 0; i < num_indexes; ++i) {
154
87.4k
    if (!out->WriteU16(this->glyph_name_index[i])) {
155
0
      return Error("Failed to write name index %d", i);
156
0
    }
157
87.4k
  }
158
159
  // Now we just have to write out the strings in the correct order
160
113k
  for (unsigned i = 0; i < this->names.size(); ++i) {
161
112k
    const std::string& s = this->names[i];
162
112k
    const uint8_t string_length = static_cast<uint8_t>(s.size());
163
112k
    if (string_length != s.size() ||
164
112k
        !out->Write(&string_length, 1)) {
165
0
      return Error("Failed to write string %d", i);
166
0
    }
167
    // Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
168
    // We allow them.
169
112k
    if (string_length > 0 && !out->Write(s.data(), string_length)) {
170
0
      return Error("Failed to write string length for string %d", i);
171
0
    }
172
112k
  }
173
174
321
  return true;
175
321
}
176
177
}  // namespace ots