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 "hdmx.h"
6#include "head.h"
7#include "maxp.h"
8
9// hdmx - Horizontal Device Metrics
10// http://www.microsoft.com/opentype/otspec/hdmx.htm
11
12#define DROP_THIS_TABLE \
13  do { delete file->hdmx; file->hdmx = 0; } while (0)
14
15namespace ots {
16
17bool ots_hdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
18  Buffer table(data, length);
19  file->hdmx = new OpenTypeHDMX;
20  OpenTypeHDMX * const hdmx = file->hdmx;
21
22  if (!file->head || !file->maxp) {
23    return OTS_FAILURE();
24  }
25
26  if ((file->head->flags & 0x14) == 0) {
27    // http://www.microsoft.com/typography/otspec/recom.htm
28    OTS_WARNING("the table should not be present when bit 2 and 4 of the "
29                "head->flags are not set");
30    DROP_THIS_TABLE;
31    return true;
32  }
33
34  int16_t num_recs;
35  if (!table.ReadU16(&hdmx->version) ||
36      !table.ReadS16(&num_recs) ||
37      !table.ReadS32(&hdmx->size_device_record)) {
38    return OTS_FAILURE();
39  }
40  if (hdmx->version != 0) {
41    OTS_WARNING("bad version: %u", hdmx->version);
42    DROP_THIS_TABLE;
43    return true;
44  }
45  if (num_recs <= 0) {
46    OTS_WARNING("bad num_recs: %d", num_recs);
47    DROP_THIS_TABLE;
48    return true;
49  }
50  const int32_t actual_size_device_record = file->maxp->num_glyphs + 2;
51  if (hdmx->size_device_record < actual_size_device_record) {
52    OTS_WARNING("bad hdmx->size_device_record: %d", hdmx->size_device_record);
53    DROP_THIS_TABLE;
54    return true;
55  }
56
57  hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
58  if (hdmx->pad_len > 3) {
59    return OTS_FAILURE();
60  }
61
62  uint8_t last_pixel_size = 0;
63  hdmx->records.reserve(num_recs);
64  for (int i = 0; i < num_recs; ++i) {
65    OpenTypeHDMXDeviceRecord rec;
66
67    if (!table.ReadU8(&rec.pixel_size) ||
68        !table.ReadU8(&rec.max_width)) {
69      return OTS_FAILURE();
70    }
71    if ((i != 0) &&
72        (rec.pixel_size <= last_pixel_size)) {
73      OTS_WARNING("records are not sorted");
74      DROP_THIS_TABLE;
75      return true;
76    }
77    last_pixel_size = rec.pixel_size;
78
79    rec.widths.reserve(file->maxp->num_glyphs);
80    for (unsigned j = 0; j < file->maxp->num_glyphs; ++j) {
81      uint8_t width;
82      if (!table.ReadU8(&width)) {
83        return OTS_FAILURE();
84      }
85      rec.widths.push_back(width);
86    }
87
88    if ((hdmx->pad_len > 0) &&
89        !table.Skip(hdmx->pad_len)) {
90      return OTS_FAILURE();
91    }
92
93    hdmx->records.push_back(rec);
94  }
95
96  return true;
97}
98
99bool ots_hdmx_should_serialise(OpenTypeFile *file) {
100  if (!file->hdmx) return false;
101  if (!file->glyf) return false;  // this table is not for CFF fonts.
102  return true;
103}
104
105bool ots_hdmx_serialise(OTSStream *out, OpenTypeFile *file) {
106  OpenTypeHDMX * const hdmx = file->hdmx;
107
108  if (!out->WriteU16(hdmx->version) ||
109      !out->WriteS16(hdmx->records.size()) ||
110      !out->WriteS32(hdmx->size_device_record)) {
111    return OTS_FAILURE();
112  }
113
114  for (unsigned i = 0; i < hdmx->records.size(); ++i) {
115    const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
116    if (!out->Write(&rec.pixel_size, 1) ||
117        !out->Write(&rec.max_width, 1) ||
118        !out->Write(&rec.widths[0], rec.widths.size())) {
119      return OTS_FAILURE();
120    }
121    if ((hdmx->pad_len > 0) &&
122        !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
123      return OTS_FAILURE();
124    }
125  }
126
127  return true;
128}
129
130void ots_hdmx_free(OpenTypeFile *file) {
131  delete file->hdmx;
132}
133
134}  // namespace ots
135