Imported Upstream version 1.8.1
[platform/upstream/harfbuzz.git] / src / hb-ot-hdmx-table.hh
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-private.hh"
31 #include "hb-subset-plan.hh"
32
33 /*
34  * hdmx -- Horizontal Device Metrics
35  * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx
36  */
37 #define HB_OT_TAG_hdmx HB_TAG('h','d','m','x')
38
39
40 namespace OT {
41
42
43 struct DeviceRecord
44 {
45   struct SubsetView
46   {
47     const DeviceRecord *source_device_record;
48     unsigned int size_device_record;
49     hb_subset_plan_t *subset_plan;
50
51     inline void init(const DeviceRecord *source_device_record,
52                      unsigned int size_device_record,
53                      hb_subset_plan_t   *subset_plan)
54     {
55       this->source_device_record = source_device_record;
56       this->size_device_record = size_device_record;
57       this->subset_plan = subset_plan;
58     }
59
60     inline unsigned int len () const
61     {
62       return this->subset_plan->glyphs.len;
63     }
64
65     inline const HBUINT8* operator [] (unsigned int i) const
66     {
67       if (unlikely (i >= len())) return nullptr;
68       hb_codepoint_t gid = this->subset_plan->glyphs [i];
69
70       const HBUINT8* width = &(this->source_device_record->widths[gid]);
71
72       if (width < ((const HBUINT8 *) this->source_device_record) + size_device_record)
73         return width;
74       else
75         return nullptr;
76     }
77   };
78
79   static inline unsigned int get_size (unsigned int count)
80   {
81     unsigned int raw_size = min_size + count * HBUINT8::static_size;
82     if (raw_size % 4)
83       /* Align to 32 bits */
84       return raw_size + (4 - (raw_size % 4));
85     return raw_size;
86   }
87
88   inline bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
89   {
90     TRACE_SERIALIZE (this);
91
92     if (unlikely (!c->allocate_size<DeviceRecord> (get_size (subset_view.len()))))
93       return_trace (false);
94
95     this->pixel_size.set (subset_view.source_device_record->pixel_size);
96     this->max_width.set (subset_view.source_device_record->max_width);
97
98     for (unsigned int i = 0; i < subset_view.len(); i++)
99     {
100       const HBUINT8 *width = subset_view[i];
101       if (!width)
102       {
103         DEBUG_MSG(SUBSET, nullptr, "HDMX width for new gid %d is missing.", i);
104         return_trace (false);
105       }
106       widths[i].set (*width);
107     }
108
109     return_trace (true);
110   }
111
112   inline bool sanitize (hb_sanitize_context_t *c, unsigned int size_device_record) const
113   {
114     TRACE_SANITIZE (this);
115     return_trace (likely (c->check_struct (this) &&
116                           c->check_range (this, size_device_record)));
117   }
118
119   HBUINT8 pixel_size;   /* Pixel size for following widths (as ppem). */
120   HBUINT8 max_width;    /* Maximum width. */
121   HBUINT8 widths[VAR];  /* Array of widths (numGlyphs is from the 'maxp' table). */
122   public:
123   DEFINE_SIZE_ARRAY (2, widths);
124 };
125
126
127 struct hdmx
128 {
129   static const hb_tag_t tableTag = HB_OT_TAG_hdmx;
130
131   inline unsigned int get_size (void) const
132   {
133     return min_size + num_records * size_device_record;
134   }
135
136   inline const DeviceRecord& operator [] (unsigned int i) const
137   {
138     if (unlikely (i >= num_records)) return Null(DeviceRecord);
139     return StructAtOffset<DeviceRecord> (this->data, i * size_device_record);
140   }
141
142   inline bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
143   {
144     TRACE_SERIALIZE (this);
145
146     if (unlikely (!c->extend_min ((*this))))  return_trace (false);
147
148     this->version.set (source_hdmx->version);
149     this->num_records.set (source_hdmx->num_records);
150     this->size_device_record.set (DeviceRecord::get_size (plan->glyphs.len));
151
152     for (unsigned int i = 0; i < source_hdmx->num_records; i++)
153     {
154       DeviceRecord::SubsetView subset_view;
155       subset_view.init (&(*source_hdmx)[i], source_hdmx->size_device_record, plan);
156
157       if (!c->start_embed<DeviceRecord> ()->serialize (c, subset_view))
158         return_trace (false);
159     }
160
161     return_trace (true);
162   }
163
164   static inline size_t get_subsetted_size (hb_subset_plan_t *plan)
165   {
166     return min_size + DeviceRecord::get_size (plan->glyphs.len);
167   }
168
169   inline bool subset (hb_subset_plan_t *plan) const
170   {
171     size_t dest_size = get_subsetted_size (plan);
172     hdmx *dest = (hdmx *) malloc (dest_size);
173     if (unlikely (!dest))
174     {
175       DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size);
176       return false;
177     }
178
179     hb_serialize_context_t c (dest, dest_size);
180     hdmx *hdmx_prime = c.start_serialize<hdmx> ();
181     if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) {
182       free (dest);
183       return false;
184     }
185     c.end_serialize ();
186
187     hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest,
188                                                  dest_size,
189                                                  HB_MEMORY_MODE_READONLY,
190                                                  dest,
191                                                  free);
192     bool result = plan->add_table (HB_OT_TAG_hdmx, hdmx_prime_blob);
193     hb_blob_destroy (hdmx_prime_blob);
194
195     return result;
196   }
197
198   inline bool sanitize (hb_sanitize_context_t *c) const
199   {
200     TRACE_SANITIZE (this);
201     return_trace (c->check_struct (this) && version == 0 &&
202                   !_hb_unsigned_int_mul_overflows (num_records, size_device_record) &&
203                   size_device_record >= DeviceRecord::min_size &&
204                   c->check_range (this, get_size()));
205   }
206
207   protected:
208   HBUINT16      version;                /* Table version number (0) */
209   HBUINT16      num_records;            /* Number of device records. */
210   HBUINT32      size_device_record;     /* Size of a device record, 32-bit aligned. */
211   HBUINT8       data[VAR];              /* Array of device records. */
212   public:
213   DEFINE_SIZE_ARRAY (8, data);
214 };
215
216 } /* namespace OT */
217
218
219 #endif /* HB_OT_HDMX_TABLE_HH */