1 | /* |
2 | * Copyright © 2018 Google, Inc. |
3 | * |
4 | * This is part of HarfBuzz, a text shaping library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | * |
24 | * Google Author(s): Garret Rieger |
25 | */ |
26 | |
27 | #ifndef HB_OT_HDMX_TABLE_HH |
28 | #define HB_OT_HDMX_TABLE_HH |
29 | |
30 | #include "hb-open-type.hh" |
31 | |
32 | /* |
33 | * hdmx -- Horizontal Device Metrics |
34 | * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx |
35 | */ |
36 | #define HB_OT_TAG_hdmx HB_TAG('h','d','m','x') |
37 | |
38 | |
39 | namespace OT { |
40 | |
41 | |
42 | struct DeviceRecord |
43 | { |
44 | static unsigned int get_size (unsigned count) |
45 | { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } |
46 | |
47 | template<typename Iterator, |
48 | hb_requires (hb_is_iterator (Iterator))> |
49 | bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it) |
50 | { |
51 | TRACE_SERIALIZE (this); |
52 | |
53 | unsigned length = it.len (); |
54 | |
55 | if (unlikely (!c->extend (*this, length))) return_trace (false); |
56 | |
57 | this->pixelSize = pixelSize; |
58 | this->maxWidth = |
59 | + it |
60 | | hb_reduce (hb_max, 0u); |
61 | |
62 | + it |
63 | | hb_sink (widthsZ.as_array (length)); |
64 | |
65 | return_trace (true); |
66 | } |
67 | |
68 | bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const |
69 | { |
70 | TRACE_SANITIZE (this); |
71 | return_trace (likely (c->check_struct (this) && |
72 | c->check_range (this, sizeDeviceRecord))); |
73 | } |
74 | |
75 | HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ |
76 | HBUINT8 maxWidth; /* Maximum width. */ |
77 | UnsizedArrayOf<HBUINT8> widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */ |
78 | public: |
79 | DEFINE_SIZE_ARRAY (2, widthsZ); |
80 | }; |
81 | |
82 | |
83 | struct hdmx |
84 | { |
85 | static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx; |
86 | |
87 | unsigned int get_size () const |
88 | { return min_size + numRecords * sizeDeviceRecord; } |
89 | |
90 | const DeviceRecord& operator [] (unsigned int i) const |
91 | { |
92 | /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed. |
93 | * https://github.com/harfbuzz/harfbuzz/issues/1300 */ |
94 | if (unlikely (i >= numRecords)) return Null (DeviceRecord); |
95 | return StructAtOffset<DeviceRecord> (&this->firstDeviceRecord, i * sizeDeviceRecord); |
96 | } |
97 | |
98 | template<typename Iterator, |
99 | hb_requires (hb_is_iterator (Iterator))> |
100 | bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) |
101 | { |
102 | TRACE_SERIALIZE (this); |
103 | |
104 | if (unlikely (!c->extend_min ((*this)))) return_trace (false); |
105 | |
106 | this->version = version; |
107 | this->numRecords = it.len (); |
108 | this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0); |
109 | |
110 | + it |
111 | | hb_apply ([c] (const hb_item_type<Iterator>& _) { |
112 | c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second); |
113 | }) |
114 | ; |
115 | |
116 | return_trace (c->successful); |
117 | } |
118 | |
119 | |
120 | bool subset (hb_subset_context_t *c) const |
121 | { |
122 | TRACE_SUBSET (this); |
123 | |
124 | hdmx *hdmx_prime = c->serializer->start_embed <hdmx> (); |
125 | if (unlikely (!hdmx_prime)) return_trace (false); |
126 | |
127 | auto it = |
128 | + hb_range ((unsigned) numRecords) |
129 | | hb_map ([c, this] (unsigned _) |
130 | { |
131 | const DeviceRecord *device_record = |
132 | &StructAtOffset<DeviceRecord> (&firstDeviceRecord, |
133 | _ * sizeDeviceRecord); |
134 | auto row = |
135 | + hb_range (c->plan->num_output_glyphs ()) |
136 | | hb_map (c->plan->reverse_glyph_map) |
137 | | hb_map ([=] (hb_codepoint_t _) |
138 | { |
139 | if (c->plan->is_empty_glyph (_)) |
140 | return Null(HBUINT8); |
141 | return device_record->widthsZ.as_array (get_num_glyphs ()) [_]; |
142 | }) |
143 | ; |
144 | return hb_pair ((unsigned) device_record->pixelSize, +row); |
145 | }) |
146 | ; |
147 | |
148 | hdmx_prime->serialize (c->serializer, version, it); |
149 | return_trace (true); |
150 | } |
151 | |
152 | unsigned get_num_glyphs () const |
153 | { |
154 | return sizeDeviceRecord - DeviceRecord::min_size; |
155 | } |
156 | |
157 | bool sanitize (hb_sanitize_context_t *c) const |
158 | { |
159 | TRACE_SANITIZE (this); |
160 | return_trace (c->check_struct (this) && |
161 | !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && |
162 | sizeDeviceRecord >= DeviceRecord::min_size && |
163 | c->check_range (this, get_size ())); |
164 | } |
165 | |
166 | protected: |
167 | HBUINT16 version; /* Table version number (0) */ |
168 | HBUINT16 numRecords; /* Number of device records. */ |
169 | HBUINT32 sizeDeviceRecord; /* Size of a device record, 32-bit aligned. */ |
170 | DeviceRecord firstDeviceRecord; /* Array of device records. */ |
171 | public: |
172 | DEFINE_SIZE_MIN (8); |
173 | }; |
174 | |
175 | } /* namespace OT */ |
176 | |
177 | |
178 | #endif /* HB_OT_HDMX_TABLE_HH */ |
179 | |