b97c7633eb8a5066868aafc5fe4352ecf4cdb46c
[platform/upstream/harfbuzz.git] / src / hb-subset.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, Rod Sheeter, Behdad Esfahbod
25  */
26
27 #include "hb-private.hh"
28 #include "hb-object-private.hh"
29 #include "hb-open-type-private.hh"
30
31 #include "hb-subset-glyf.hh"
32 #include "hb-subset-private.hh"
33 #include "hb-subset-plan.hh"
34
35 #include "hb-open-file-private.hh"
36 #include "hb-ot-cmap-table.hh"
37 #include "hb-ot-glyf-table.hh"
38 #include "hb-ot-hdmx-table.hh"
39 #include "hb-ot-head-table.hh"
40 #include "hb-ot-hhea-table.hh"
41 #include "hb-ot-hmtx-table.hh"
42 #include "hb-ot-maxp-table.hh"
43 #include "hb-ot-os2-table.hh"
44 #include "hb-ot-post-table.hh"
45
46
47 struct hb_subset_profile_t {
48   hb_object_header_t header;
49   ASSERT_POD ();
50 };
51
52 /**
53  * hb_subset_profile_create:
54  *
55  * Return value: New profile with default settings.
56  *
57  * Since: 1.8.0
58  **/
59 hb_subset_profile_t *
60 hb_subset_profile_create ()
61 {
62   return hb_object_create<hb_subset_profile_t>();
63 }
64
65 /**
66  * hb_subset_profile_destroy:
67  *
68  * Since: 1.8.0
69  **/
70 void
71 hb_subset_profile_destroy (hb_subset_profile_t *profile)
72 {
73   if (!hb_object_destroy (profile)) return;
74
75   free (profile);
76 }
77
78 template<typename TableType>
79 static bool
80 _subset (hb_subset_plan_t *plan)
81 {
82   OT::Sanitizer<TableType> sanitizer;
83
84   hb_blob_t *source_blob = sanitizer.sanitize (plan->source->reference_table (TableType::tableTag));
85   const TableType *table = source_blob->as<TableType> ();
86
87   hb_tag_t tag = TableType::tableTag;
88   hb_bool_t result = false;
89   if (table != &Null(TableType))
90   {
91     result = table->subset(plan);
92   } else {
93     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG(tag));
94   }
95
96   hb_blob_destroy (source_blob);
97   DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
98   return result;
99 }
100
101
102 /*
103  * A face that has add_table().
104  */
105
106 struct hb_subset_face_data_t
107 {
108   struct table_entry_t
109   {
110     inline int cmp (const hb_tag_t *t) const
111     {
112       if (*t < tag) return -1;
113       if (*t > tag) return -1;
114       return 0;
115     }
116
117     hb_tag_t   tag;
118     hb_blob_t *blob;
119   };
120
121   hb_vector_t<table_entry_t, 32> tables;
122 };
123
124 static hb_subset_face_data_t *
125 _hb_subset_face_data_create (void)
126 {
127   hb_subset_face_data_t *data = (hb_subset_face_data_t *) calloc (1, sizeof (hb_subset_face_data_t));
128   if (unlikely (!data))
129     return nullptr;
130
131   data->tables.init ();
132
133   return data;
134 }
135
136 static void
137 _hb_subset_face_data_destroy (void *user_data)
138 {
139   hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;
140
141   for (unsigned int i = 0; i < data->tables.len; i++)
142     hb_blob_destroy (data->tables[i].blob);
143
144   data->tables.fini ();
145
146   free (data);
147 }
148
149 static hb_blob_t *
150 _hb_subset_face_data_reference_blob (hb_subset_face_data_t *data)
151 {
152
153   unsigned int table_count = data->tables.len;
154   unsigned int face_length = table_count * 16 + 12;
155
156   for (unsigned int i = 0; i < table_count; i++)
157     face_length += _hb_ceil_to_4 (hb_blob_get_length (data->tables.arrayZ[i].blob));
158
159   char *buf = (char *) malloc (face_length);
160   if (unlikely (!buf))
161     return nullptr;
162
163   OT::hb_serialize_context_t c (buf, face_length);
164   OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
165
166   bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
167   hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
168
169   OT::Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
170   OT::Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
171   bool ret = f->serialize_single (&c,
172                                   sfnt_tag,
173                                   tags_supplier,
174                                   blobs_supplier,
175                                   table_count);
176
177   c.end_serialize ();
178
179   if (unlikely (!ret))
180   {
181     free (buf);
182     return nullptr;
183   }
184
185   return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free);
186 }
187
188 static hb_blob_t *
189 _hb_subset_face_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data)
190 {
191   hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;
192
193   if (!tag)
194     return _hb_subset_face_data_reference_blob (data);
195
196   hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (tag);
197   if (entry)
198     return hb_blob_reference (entry->blob);
199
200   return nullptr;
201 }
202
203 /* TODO: Move this to hb-face.h and rename to hb_face_builder_create()
204  * with hb_face_builder_add_table(). */
205 hb_face_t *
206 hb_subset_face_create (void)
207 {
208   hb_subset_face_data_t *data = _hb_subset_face_data_create ();
209   if (unlikely (!data)) return hb_face_get_empty ();
210
211   return hb_face_create_for_tables (_hb_subset_face_reference_table,
212                                     data,
213                                     _hb_subset_face_data_destroy);
214 }
215
216 hb_bool_t
217 hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
218 {
219   if (unlikely (face->destroy != (hb_destroy_func_t) _hb_subset_face_data_destroy))
220     return false;
221
222   hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data;
223   hb_subset_face_data_t::table_entry_t *entry = data->tables.push ();
224
225   entry->tag = tag;
226   entry->blob = hb_blob_reference (blob);
227
228   return true;
229 }
230
231 static bool
232 _subset_table (hb_subset_plan_t *plan,
233                hb_tag_t          tag)
234 {
235   DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag));
236   bool result = true;
237   switch (tag) {
238     case HB_OT_TAG_glyf:
239       result = _subset<const OT::glyf> (plan);
240       break;
241     case HB_OT_TAG_hdmx:
242       result = _subset<const OT::hdmx> (plan);
243       break;
244     case HB_OT_TAG_head:
245       // TODO that won't work well if there is no glyf
246       DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");
247       result = true;
248       break;
249     case HB_OT_TAG_hhea:
250       DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx");
251       return true;
252     case HB_OT_TAG_hmtx:
253       result = _subset<const OT::hmtx> (plan);
254       break;
255     case HB_OT_TAG_vhea:
256       DEBUG_MSG(SUBSET, nullptr, "skip vhea handled by vmtx");
257       return true;
258     case HB_OT_TAG_vmtx:
259       result = _subset<const OT::vmtx> (plan);
260       break;
261     case HB_OT_TAG_maxp:
262       result = _subset<const OT::maxp> (plan);
263       break;
264     case HB_OT_TAG_loca:
265       DEBUG_MSG(SUBSET, nullptr, "skip loca handled by glyf");
266       return true;
267     case HB_OT_TAG_cmap:
268       result = _subset<const OT::cmap> (plan);
269       break;
270     case HB_OT_TAG_os2:
271       result = _subset<const OT::os2> (plan);
272       break;
273     case HB_OT_TAG_post:
274       result = _subset<const OT::post> (plan);
275       break;
276     default:
277       hb_blob_t *source_table = hb_face_reference_table(plan->source, tag);
278       if (likely (source_table))
279         result = plan->add_table(tag, source_table);
280       else
281         result = false;
282       hb_blob_destroy (source_table);
283       break;
284   }
285   DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), result ? "ok" : "FAILED");
286   return result;
287 }
288
289 static bool
290 _should_drop_table(hb_subset_plan_t *plan, hb_tag_t tag)
291 {
292   switch (tag) {
293     case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */
294     case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */
295     case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */
296     case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */
297     case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */
298     case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
299       return plan->drop_hints;
300     // Drop Layout Tables if requested.
301     case HB_TAG ('G', 'D', 'E', 'F'): /* temporary */
302     case HB_TAG ('G', 'P', 'O', 'S'): /* temporary */
303     case HB_TAG ('G', 'S', 'U', 'B'): /* temporary */
304       return plan->drop_ot_layout;
305     // Drop these tables below by default, list pulled
306     // from fontTools:
307     case HB_TAG ('B', 'A', 'S', 'E'):
308     case HB_TAG ('J', 'S', 'T', 'F'):
309     case HB_TAG ('D', 'S', 'I', 'G'):
310     case HB_TAG ('E', 'B', 'D', 'T'):
311     case HB_TAG ('E', 'B', 'L', 'C'):
312     case HB_TAG ('E', 'B', 'S', 'C'):
313     case HB_TAG ('S', 'V', 'G', ' '):
314     case HB_TAG ('P', 'C', 'L', 'T'):
315     case HB_TAG ('L', 'T', 'S', 'H'):
316     // Graphite tables:
317     case HB_TAG ('F', 'e', 'a', 't'):
318     case HB_TAG ('G', 'l', 'a', 't'):
319     case HB_TAG ('G', 'l', 'o', 'c'):
320     case HB_TAG ('S', 'i', 'l', 'f'):
321     case HB_TAG ('S', 'i', 'l', 'l'):
322     // Colour
323     case HB_TAG ('s', 'b', 'i', 'x'):
324       return true;
325     default:
326       return false;
327   }
328 }
329
330 /**
331  * hb_subset:
332  * @source: font face data to be subset.
333  * @profile: profile to use for the subsetting.
334  * @input: input to use for the subsetting.
335  *
336  * Subsets a font according to provided profile and input.
337  **/
338 hb_face_t *
339 hb_subset (hb_face_t *source,
340            hb_subset_profile_t *profile,
341            hb_subset_input_t *input)
342 {
343   if (unlikely (!profile || !input || !source)) return hb_face_get_empty();
344
345   hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
346
347   hb_tag_t table_tags[32];
348   unsigned int offset = 0, count;
349   bool success = true;
350   do {
351     count = ARRAY_LENGTH (table_tags);
352     hb_face_get_table_tags (source, offset, &count, table_tags);
353     for (unsigned int i = 0; i < count; i++)
354     {
355       hb_tag_t tag = table_tags[i];
356       if (_should_drop_table(plan, tag))
357       {
358         DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG(tag));
359         continue;
360       }
361       success = success && _subset_table (plan, tag);
362     }
363     offset += count;
364   } while (count == ARRAY_LENGTH (table_tags));
365
366   hb_face_t *result = success ? hb_face_reference(plan->dest) : hb_face_get_empty();
367   hb_subset_plan_destroy (plan);
368   return result;
369 }
370
371 /**
372  * hb_subset_get_all_codepoints:
373  * @source: font face data to load.
374  * @out: set to add the all codepoints covered by font face, source.
375  */
376 void
377 hb_subset_get_all_codepoints (hb_face_t *source, hb_set_t *out)
378 {
379   OT::cmap::accelerator_t cmap;
380   cmap.init (source);
381   cmap.get_all_codepoints (out);
382   cmap.fini();
383 }