Imported Upstream version 2.4.0
[platform/upstream/harfbuzz.git] / src / hb-subset-glyf.cc
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, Roderick Sheeter
25  */
26
27 #include "hb-open-type.hh"
28 #include "hb-ot-glyf-table.hh"
29 #include "hb-set.h"
30 #include "hb-subset-glyf.hh"
31
32 struct loca_data_t
33 {
34   bool          is_short;
35   void         *data;
36   unsigned int  size;
37
38   inline bool
39   _write_loca_entry (unsigned int  id,
40                      unsigned int  offset)
41   {
42     unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
43     if ((id + 1) * entry_size <= size)
44     {
45       if (is_short) {
46         ((OT::HBUINT16*) data) [id].set (offset / 2);
47       } else {
48         ((OT::HBUINT32*) data) [id].set (offset);
49       }
50       return true;
51     }
52
53     // Offset was not written because the write is out of bounds.
54     DEBUG_MSG(SUBSET,
55               nullptr,
56               "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
57               id,
58               size);
59     return false;
60   }
61 };
62
63 /**
64  * If hints are being dropped find the range which in glyf at which
65  * the hinting instructions are located. Add them to the instruction_ranges
66  * vector.
67  */
68 static bool
69 _add_instructions_range (const OT::glyf::accelerator_t &glyf,
70                          hb_codepoint_t                 glyph_id,
71                          unsigned int                   glyph_start_offset,
72                          unsigned int                   glyph_end_offset,
73                          bool                           drop_hints,
74                          hb_vector_t<unsigned int>     *instruction_ranges /* OUT */)
75 {
76   if (!instruction_ranges->resize (instruction_ranges->length + 2))
77   {
78     DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges.");
79     return false;
80   }
81   unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2];
82   *instruction_start = 0;
83   unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1];
84   *instruction_end = 0;
85
86   if (drop_hints)
87   {
88     if (unlikely (!glyf.get_instruction_offsets (glyph_start_offset, glyph_end_offset,
89                                                  instruction_start, instruction_end)))
90     {
91       DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", glyph_id);
92       return false;
93     }
94   }
95
96   return true;
97 }
98
99 static bool
100 _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
101                                      const hb_subset_plan_t        *plan,
102                                      loca_data_t                   *loca_data, /* OUT */
103                                      unsigned int                  *glyf_size /* OUT */,
104                                      hb_vector_t<unsigned int>     *instruction_ranges /* OUT */)
105 {
106   unsigned int total = 0;
107
108   hb_codepoint_t next_glyph = HB_SET_VALUE_INVALID;
109   while (plan->glyphset ()->next (&next_glyph))
110   {
111     unsigned int start_offset, end_offset;
112     if (unlikely (!(glyf.get_offsets (next_glyph, &start_offset, &end_offset) &&
113                     glyf.remove_padding (start_offset, &end_offset))))
114     {
115       DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph);
116       start_offset = end_offset = 0;
117     }
118
119     bool is_zero_length = end_offset - start_offset < OT::glyf::GlyphHeader::static_size;
120     if (!_add_instructions_range (glyf,
121                                   next_glyph,
122                                   start_offset,
123                                   end_offset,
124                                   plan->drop_hints && !is_zero_length,
125                                   instruction_ranges))
126       return false;
127
128     if (is_zero_length)
129       continue; /* 0-length glyph */
130
131     total += end_offset - start_offset
132              - ((*instruction_ranges)[instruction_ranges->length - 1]
133                 - (*instruction_ranges)[instruction_ranges->length - 2]);
134     /* round2 so short loca will work */
135     total += total % 2;
136   }
137
138   *glyf_size = total;
139   loca_data->is_short = (total <= 131070);
140   loca_data->size = (plan->num_output_glyphs () + 1)
141       * (loca_data->is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32));
142
143   DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca",
144             total,
145             loca_data->size,
146             loca_data->is_short ? "short" : "long");
147   return true;
148 }
149
150 static void
151 _update_components (const hb_subset_plan_t *plan,
152                     char                   *glyph_start,
153                     unsigned int            length)
154 {
155   OT::glyf::CompositeGlyphHeader::Iterator iterator;
156   if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start,
157                                                     length,
158                                                     &iterator))
159   {
160     do
161     {
162       hb_codepoint_t new_gid;
163       if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex,
164                                       &new_gid))
165         continue;
166
167       ((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex.set (new_gid);
168     } while (iterator.move_to_next ());
169   }
170 }
171
172 static bool _remove_composite_instruction_flag (char *glyf_prime, unsigned int length)
173 {
174   /* remove WE_HAVE_INSTRUCTIONS from flags in dest */
175   OT::glyf::CompositeGlyphHeader::Iterator composite_it;
176   if (unlikely (!OT::glyf::CompositeGlyphHeader::get_iterator (glyf_prime, length, &composite_it))) return false;
177   const OT::glyf::CompositeGlyphHeader *glyph;
178   do {
179     glyph = composite_it.current;
180     OT::HBUINT16 *flags = const_cast<OT::HBUINT16 *> (&glyph->flags);
181     flags->set ( (uint16_t) *flags & ~OT::glyf::CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS);
182   } while (composite_it.move_to_next ());
183   return true;
184 }
185
186 static bool
187 _write_glyf_and_loca_prime (const hb_subset_plan_t        *plan,
188                             const OT::glyf::accelerator_t &glyf,
189                             const char                    *glyf_data,
190                             hb_vector_t<unsigned int>     &instruction_ranges,
191                             unsigned int                   glyf_prime_size,
192                             char                          *glyf_prime_data /* OUT */,
193                             loca_data_t                   *loca_prime /* OUT */)
194 {
195   char *glyf_prime_data_next = glyf_prime_data;
196
197   bool success = true;
198
199
200   unsigned int i = 0;
201   hb_codepoint_t new_gid;
202   for (new_gid = 0; new_gid < plan->num_output_glyphs (); new_gid++)
203   {
204     hb_codepoint_t old_gid;
205     if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
206     {
207       // Empty glyph, add a loca entry and carry on.
208       loca_prime->_write_loca_entry (new_gid,
209                                      glyf_prime_data_next - glyf_prime_data);
210       continue;
211     }
212
213
214     unsigned int start_offset, end_offset;
215     if (unlikely (!(glyf.get_offsets (old_gid, &start_offset, &end_offset) &&
216                     glyf.remove_padding (start_offset, &end_offset))))
217       end_offset = start_offset = 0;
218
219     unsigned int instruction_start = instruction_ranges[i * 2];
220     unsigned int instruction_end = instruction_ranges[i * 2 + 1];
221
222     int length = end_offset - start_offset - (instruction_end - instruction_start);
223
224     if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size)
225     {
226       DEBUG_MSG(SUBSET,
227                 nullptr,
228                 "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
229                 i, length);
230       return false;
231     }
232
233     if (instruction_start == instruction_end)
234       memcpy (glyf_prime_data_next, glyf_data + start_offset, length);
235     else
236     {
237       memcpy (glyf_prime_data_next, glyf_data + start_offset, instruction_start - start_offset);
238       memcpy (glyf_prime_data_next + instruction_start - start_offset, glyf_data + instruction_end, end_offset - instruction_end);
239       /* if the instructions end at the end this was a composite glyph, else simple */
240       if (instruction_end == end_offset)
241       {
242         if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false;
243       }
244       else
245         /* zero instruction length, which is just before instruction_start */
246         memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2);
247     }
248
249     success = success && loca_prime->_write_loca_entry (new_gid,
250                                                         glyf_prime_data_next - glyf_prime_data);
251     _update_components (plan, glyf_prime_data_next, length);
252
253     // TODO: don't align to two bytes if using long loca.
254     glyf_prime_data_next += length + (length % 2); // Align to 2 bytes for short loca.
255
256     i++;
257   }
258
259   // loca table has n+1 entries where the last entry signifies the end location of the last
260   // glyph.
261   success = success && loca_prime->_write_loca_entry (new_gid,
262                                                       glyf_prime_data_next - glyf_prime_data);
263   return success;
264 }
265
266 static bool
267 _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
268                           const char                     *glyf_data,
269                           hb_subset_plan_t               *plan,
270                           bool                           *use_short_loca,
271                           hb_blob_t                     **glyf_prime_blob /* OUT */,
272                           hb_blob_t                     **loca_prime_blob /* OUT */)
273 {
274   // TODO(grieger): Sanity check allocation size for the new table.
275   loca_data_t loca_prime;
276   unsigned int glyf_prime_size;
277   hb_vector_t<unsigned int> instruction_ranges;
278   instruction_ranges.init ();
279
280   if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf,
281                                                       plan,
282                                                       &loca_prime,
283                                                       &glyf_prime_size,
284                                                       &instruction_ranges))) {
285     instruction_ranges.fini ();
286     return false;
287   }
288   *use_short_loca = loca_prime.is_short;
289
290   char *glyf_prime_data = (char *) calloc (1, glyf_prime_size);
291   loca_prime.data = (void *) calloc (1, loca_prime.size);
292   if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data,
293                                              instruction_ranges,
294                                              glyf_prime_size, glyf_prime_data,
295                                              &loca_prime))) {
296     free (glyf_prime_data);
297     free (loca_prime.data);
298     instruction_ranges.fini ();
299     return false;
300   }
301   instruction_ranges.fini ();
302
303   *glyf_prime_blob = hb_blob_create (glyf_prime_data,
304                                      glyf_prime_size,
305                                      HB_MEMORY_MODE_READONLY,
306                                      glyf_prime_data,
307                                      free);
308   *loca_prime_blob = hb_blob_create ((char *) loca_prime.data,
309                                      loca_prime.size,
310                                      HB_MEMORY_MODE_READONLY,
311                                      loca_prime.data,
312                                      free);
313   return true;
314 }
315
316 /**
317  * hb_subset_glyf:
318  * Subsets the glyph table according to a provided plan.
319  *
320  * Return value: subsetted glyf table.
321  *
322  * Since: 1.7.5
323  **/
324 bool
325 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
326                          bool             *use_short_loca, /* OUT */
327                          hb_blob_t       **glyf_prime, /* OUT */
328                          hb_blob_t       **loca_prime /* OUT */)
329 {
330   hb_blob_t *glyf_blob = hb_sanitize_context_t ().reference_table<OT::glyf> (plan->source);
331   const char *glyf_data = hb_blob_get_data (glyf_blob, nullptr);
332
333   OT::glyf::accelerator_t glyf;
334   glyf.init (plan->source);
335   bool result = _hb_subset_glyf_and_loca (glyf,
336                                           glyf_data,
337                                           plan,
338                                           use_short_loca,
339                                           glyf_prime,
340                                           loca_prime);
341
342   hb_blob_destroy (glyf_blob);
343   glyf.fini ();
344
345   return result;
346 }