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 "post.h"
6
7#include "maxp.h"
8
9// post - PostScript
10// http://www.microsoft.com/opentype/otspec/post.htm
11
12namespace ots {
13
14bool ots_post_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
15  Buffer table(data, length);
16
17  OpenTypePOST *post = new OpenTypePOST;
18  file->post = post;
19
20  if (!table.ReadU32(&post->version) ||
21      !table.ReadU32(&post->italic_angle) ||
22      !table.ReadS16(&post->underline) ||
23      !table.ReadS16(&post->underline_thickness) ||
24      !table.ReadU32(&post->is_fixed_pitch)) {
25    return OTS_FAILURE();
26  }
27
28  if (post->underline_thickness < 0) {
29    post->underline_thickness = 1;
30  }
31
32  if (post->version == 0x00010000) {
33    return true;
34  } else if (post->version == 0x00030000) {
35    return true;
36  } else if (post->version != 0x00020000) {
37    // 0x00025000 is deprecated. We don't accept it.
38    return OTS_FAILURE();
39  }
40
41  // We have a version 2 table with a list of Pascal strings at the end
42
43  // We don't care about the memory usage fields. We'll set all these to zero
44  // when serialising
45  if (!table.Skip(16)) {
46    return OTS_FAILURE();
47  }
48
49  uint16_t num_glyphs = 0;
50  if (!table.ReadU16(&num_glyphs)) {
51    return OTS_FAILURE();
52  }
53
54  if (!file->maxp) {
55    return OTS_FAILURE();
56  }
57
58  if (num_glyphs == 0) {
59    if (file->maxp->num_glyphs > 258) {
60      return OTS_FAILURE();
61    }
62    OTS_WARNING("table version is 1, but no glyf names are found");
63    // workaround for fonts in http://www.fontsquirrel.com/fontface
64    // (e.g., yataghan.ttf).
65    post->version = 0x00010000;
66    return true;
67  }
68
69  if (num_glyphs != file->maxp->num_glyphs) {
70    // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
71    return OTS_FAILURE();
72  }
73
74  post->glyph_name_index.resize(num_glyphs);
75  for (unsigned i = 0; i < num_glyphs; ++i) {
76    if (!table.ReadU16(&post->glyph_name_index[i])) {
77      return OTS_FAILURE();
78    }
79    // Note: A strict interpretation of the specification requires name indexes
80    // are less than 32768. This, however, excludes fonts like unifont.ttf
81    // which cover all of unicode.
82  }
83
84  // Now we have an array of Pascal strings. We have to check that they are all
85  // valid and read them in.
86  const size_t strings_offset = table.offset();
87  const uint8_t *strings = data + strings_offset;
88  const uint8_t *strings_end = data + length;
89
90  for (;;) {
91    if (strings == strings_end) break;
92    const unsigned string_length = *strings;
93    if (strings + 1 + string_length > strings_end) {
94      return OTS_FAILURE();
95    }
96    if (std::memchr(strings + 1, '\0', string_length)) {
97      return OTS_FAILURE();
98    }
99    post->names.push_back(
100        std::string(reinterpret_cast<const char*>(strings + 1), string_length));
101    strings += 1 + string_length;
102  }
103  const unsigned num_strings = post->names.size();
104
105  // check that all the references are within bounds
106  for (unsigned i = 0; i < num_glyphs; ++i) {
107    unsigned offset = post->glyph_name_index[i];
108    if (offset < 258) {
109      continue;
110    }
111
112    offset -= 258;
113    if (offset >= num_strings) {
114      return OTS_FAILURE();
115    }
116  }
117
118  return true;
119}
120
121bool ots_post_should_serialise(OpenTypeFile *file) {
122  return file->post != NULL;
123}
124
125bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) {
126  const OpenTypePOST *post = file->post;
127
128  // OpenType with CFF glyphs must have v3 post table.
129  if (file->post && file->cff && file->post->version != 0x00030000) {
130    return OTS_FAILURE();
131  }
132
133  if (!out->WriteU32(post->version) ||
134      !out->WriteU32(post->italic_angle) ||
135      !out->WriteS16(post->underline) ||
136      !out->WriteS16(post->underline_thickness) ||
137      !out->WriteU32(post->is_fixed_pitch) ||
138      !out->WriteU32(0) ||
139      !out->WriteU32(0) ||
140      !out->WriteU32(0) ||
141      !out->WriteU32(0)) {
142    return OTS_FAILURE();
143  }
144
145  if (post->version != 0x00020000) {
146    return true;  // v1.0 and v3.0 does not have glyph names.
147  }
148
149  if (!out->WriteU16(post->glyph_name_index.size())) {
150    return OTS_FAILURE();
151  }
152
153  for (unsigned i = 0; i < post->glyph_name_index.size(); ++i) {
154    if (!out->WriteU16(post->glyph_name_index[i])) {
155      return OTS_FAILURE();
156    }
157  }
158
159  // Now we just have to write out the strings in the correct order
160  for (unsigned i = 0; i < post->names.size(); ++i) {
161    const std::string& s = post->names[i];
162    const uint8_t string_length = s.size();
163    if (!out->Write(&string_length, 1)) {
164      return OTS_FAILURE();
165    }
166    // Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
167    // We allow them.
168    if (string_length > 0 && !out->Write(s.data(), string_length)) {
169      return OTS_FAILURE();
170    }
171  }
172
173  return true;
174}
175
176void ots_post_free(OpenTypeFile *file) {
177  delete file->post;
178}
179
180}  // namespace ots
181