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 "gasp.h"
6
7// gasp - Grid-fitting And Scan-conversion Procedure
8// http://www.microsoft.com/opentype/otspec/gasp.htm
9
10#define DROP_THIS_TABLE \
11  do { delete file->gasp; file->gasp = 0; } while (0)
12
13namespace ots {
14
15bool ots_gasp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
16  Buffer table(data, length);
17
18  OpenTypeGASP *gasp = new OpenTypeGASP;
19  file->gasp = gasp;
20
21  uint16_t num_ranges = 0;
22  if (!table.ReadU16(&gasp->version) ||
23      !table.ReadU16(&num_ranges)) {
24    return OTS_FAILURE();
25  }
26
27  if (gasp->version > 1) {
28    // Lots of Linux fonts have bad version numbers...
29    OTS_WARNING("bad version: %u", gasp->version);
30    DROP_THIS_TABLE;
31    return true;
32  }
33
34  if (num_ranges == 0) {
35    OTS_WARNING("num_ranges is zero");
36    DROP_THIS_TABLE;
37    return true;
38  }
39
40  gasp->gasp_ranges.reserve(num_ranges);
41  for (unsigned i = 0; i < num_ranges; ++i) {
42    uint16_t max_ppem = 0;
43    uint16_t behavior = 0;
44    if (!table.ReadU16(&max_ppem) ||
45        !table.ReadU16(&behavior)) {
46      return OTS_FAILURE();
47    }
48    if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
49      // The records in the gaspRange[] array must be sorted in order of
50      // increasing rangeMaxPPEM value.
51      OTS_WARNING("ranges are not sorted");
52      DROP_THIS_TABLE;
53      return true;
54    }
55    if ((i == num_ranges - 1u) &&  // never underflow.
56        (max_ppem != 0xffffu)) {
57      OTS_WARNING("The last record should be 0xFFFF as a sentinel value "
58                  "for rangeMaxPPEM");
59      DROP_THIS_TABLE;
60      return true;
61    }
62
63    if (behavior >> 8) {
64      OTS_WARNING("undefined bits are used: %x", behavior);
65      // mask undefined bits.
66      behavior &= 0x000fu;
67    }
68
69    if (gasp->version == 0 && (behavior >> 2) != 0) {
70      OTS_WARNING("changed the version number to 1");
71      gasp->version = 1;
72    }
73
74    gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
75  }
76
77  return true;
78}
79
80bool ots_gasp_should_serialise(OpenTypeFile *file) {
81  return file->gasp != NULL;
82}
83
84bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
85  const OpenTypeGASP *gasp = file->gasp;
86
87  if (!out->WriteU16(gasp->version) ||
88      !out->WriteU16(gasp->gasp_ranges.size())) {
89    return OTS_FAILURE();
90  }
91
92  for (unsigned i = 0; i < gasp->gasp_ranges.size(); ++i) {
93    if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
94        !out->WriteU16(gasp->gasp_ranges[i].second)) {
95      return OTS_FAILURE();
96    }
97  }
98
99  return true;
100}
101
102void ots_gasp_free(OpenTypeFile *file) {
103  delete file->gasp;
104}
105
106}  // namespace ots
107