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