Imported Upstream version 2.4.0
[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.hh"
28 #include "hb-open-type.hh"
29
30 #include "hb-subset.hh"
31 #include "hb-subset-glyf.hh"
32
33 #include "hb-open-file.hh"
34 #include "hb-ot-cmap-table.hh"
35 #include "hb-ot-glyf-table.hh"
36 #include "hb-ot-hdmx-table.hh"
37 #include "hb-ot-head-table.hh"
38 #include "hb-ot-hhea-table.hh"
39 #include "hb-ot-hmtx-table.hh"
40 #include "hb-ot-maxp-table.hh"
41 #include "hb-ot-os2-table.hh"
42 #include "hb-ot-post-table.hh"
43 #include "hb-ot-cff1-table.hh"
44 #include "hb-ot-cff2-table.hh"
45 #include "hb-ot-vorg-table.hh"
46 #include "hb-ot-layout-gsub-table.hh"
47 #include "hb-ot-layout-gpos-table.hh"
48
49
50 static unsigned int
51 _plan_estimate_subset_table_size (hb_subset_plan_t *plan,
52                                   unsigned int table_len)
53 {
54   unsigned int src_glyphs = plan->source->get_num_glyphs ();
55   unsigned int dst_glyphs = plan->glyphset ()->get_population ();
56
57   if (unlikely (!src_glyphs))
58     return 512 + table_len;
59
60   return 512 + (unsigned int) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
61 }
62
63 template<typename TableType>
64 static bool
65 _subset2 (hb_subset_plan_t *plan)
66 {
67   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
68   const TableType *table = source_blob->as<TableType> ();
69
70   hb_tag_t tag = TableType::tableTag;
71   hb_bool_t result = false;
72   if (source_blob->data)
73   {
74     hb_vector_t<char> buf;
75     unsigned int buf_size = _plan_estimate_subset_table_size (plan, source_blob->length);
76     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
77     if (unlikely (!buf.alloc (buf_size)))
78     {
79       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size);
80       return false;
81     }
82   retry:
83     hb_serialize_context_t serializer ((void *) buf, buf_size);
84     hb_subset_context_t c (plan, &serializer);
85     result = table->subset (&c);
86     if (serializer.in_error ())
87     {
88       buf_size += (buf_size >> 1) + 32;
89       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size);
90       if (unlikely (!buf.alloc (buf_size)))
91       {
92         DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size);
93         return false;
94       }
95       goto retry;
96     }
97     if (result)
98     {
99       hb_blob_t *dest_blob = serializer.copy_blob ();
100       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
101       result = c.plan->add_table (tag, dest_blob);
102       hb_blob_destroy (dest_blob);
103     }
104     else
105     {
106       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
107       result = true;
108     }
109   }
110   else
111     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
112
113   hb_blob_destroy (source_blob);
114   DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!");
115   return result;
116 }
117
118 template<typename TableType>
119 static bool
120 _subset (hb_subset_plan_t *plan)
121 {
122   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
123   const TableType *table = source_blob->as<TableType> ();
124
125   hb_tag_t tag = TableType::tableTag;
126   hb_bool_t result = false;
127   if (source_blob->data)
128     result = table->subset (plan);
129   else
130     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
131
132   hb_blob_destroy (source_blob);
133   DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!");
134   return result;
135 }
136
137
138 static bool
139 _subset_table (hb_subset_plan_t *plan,
140                hb_tag_t          tag)
141 {
142   DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG (tag));
143   bool result = true;
144   switch (tag) {
145     case HB_OT_TAG_glyf:
146       result = _subset<const OT::glyf> (plan);
147       break;
148     case HB_OT_TAG_hdmx:
149       result = _subset<const OT::hdmx> (plan);
150       break;
151     case HB_OT_TAG_head:
152       // TODO that won't work well if there is no glyf
153       DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");
154       result = true;
155       break;
156     case HB_OT_TAG_hhea:
157       DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx");
158       return true;
159     case HB_OT_TAG_hmtx:
160       result = _subset<const OT::hmtx> (plan);
161       break;
162     case HB_OT_TAG_vhea:
163       DEBUG_MSG(SUBSET, nullptr, "skip vhea handled by vmtx");
164       return true;
165     case HB_OT_TAG_vmtx:
166       result = _subset<const OT::vmtx> (plan);
167       break;
168     case HB_OT_TAG_maxp:
169       result = _subset<const OT::maxp> (plan);
170       break;
171     case HB_OT_TAG_loca:
172       DEBUG_MSG(SUBSET, nullptr, "skip loca handled by glyf");
173       return true;
174     case HB_OT_TAG_cmap:
175       result = _subset<const OT::cmap> (plan);
176       break;
177     case HB_OT_TAG_OS2:
178       result = _subset<const OT::OS2> (plan);
179       break;
180     case HB_OT_TAG_post:
181       result = _subset<const OT::post> (plan);
182       break;
183     case HB_OT_TAG_cff1:
184       result = _subset<const OT::cff1> (plan);
185       break;
186     case HB_OT_TAG_cff2:
187       result = _subset<const OT::cff2> (plan);
188       break;
189     case HB_OT_TAG_VORG:
190       result = _subset<const OT::VORG> (plan);
191       break;
192     case HB_OT_TAG_GDEF:
193       result = _subset2<const OT::GDEF> (plan);
194       break;
195     case HB_OT_TAG_GSUB:
196       result = _subset2<const OT::GSUB> (plan);
197       break;
198     case HB_OT_TAG_GPOS:
199       result = _subset2<const OT::GPOS> (plan);
200       break;
201
202     default:
203       hb_blob_t *source_table = hb_face_reference_table (plan->source, tag);
204       if (likely (source_table))
205         result = plan->add_table (tag, source_table);
206       else
207         result = false;
208       hb_blob_destroy (source_table);
209       break;
210   }
211   DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG (tag), result ? "ok" : "FAILED");
212   return result;
213 }
214
215 static bool
216 _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
217 {
218   switch (tag) {
219     case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */
220     case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */
221     case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */
222     case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */
223     case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */
224     case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
225       return plan->drop_hints;
226     // Drop Layout Tables if requested.
227     case HB_OT_TAG_GDEF:
228     case HB_OT_TAG_GPOS:
229     case HB_OT_TAG_GSUB:
230       return plan->drop_layout;
231     // Drop these tables below by default, list pulled
232     // from fontTools:
233     case HB_TAG ('B', 'A', 'S', 'E'):
234     case HB_TAG ('J', 'S', 'T', 'F'):
235     case HB_TAG ('D', 'S', 'I', 'G'):
236     case HB_TAG ('E', 'B', 'D', 'T'):
237     case HB_TAG ('E', 'B', 'L', 'C'):
238     case HB_TAG ('E', 'B', 'S', 'C'):
239     case HB_TAG ('S', 'V', 'G', ' '):
240     case HB_TAG ('P', 'C', 'L', 'T'):
241     case HB_TAG ('L', 'T', 'S', 'H'):
242     // Graphite tables:
243     case HB_TAG ('F', 'e', 'a', 't'):
244     case HB_TAG ('G', 'l', 'a', 't'):
245     case HB_TAG ('G', 'l', 'o', 'c'):
246     case HB_TAG ('S', 'i', 'l', 'f'):
247     case HB_TAG ('S', 'i', 'l', 'l'):
248     // Colour
249     case HB_TAG ('s', 'b', 'i', 'x'):
250       return true;
251     default:
252       return false;
253   }
254 }
255
256 /**
257  * hb_subset:
258  * @source: font face data to be subset.
259  * @input: input to use for the subsetting.
260  *
261  * Subsets a font according to provided input.
262  **/
263 hb_face_t *
264 hb_subset (hb_face_t *source,
265            hb_subset_input_t *input)
266 {
267   if (unlikely (!input || !source)) return hb_face_get_empty ();
268
269   hb_subset_plan_t *plan = hb_subset_plan_create (source, input);
270
271   hb_tag_t table_tags[32];
272   unsigned int offset = 0, count;
273   bool success = true;
274   do {
275     count = ARRAY_LENGTH (table_tags);
276     hb_face_get_table_tags (source, offset, &count, table_tags);
277     for (unsigned int i = 0; i < count; i++)
278     {
279       hb_tag_t tag = table_tags[i];
280       if (_should_drop_table (plan, tag))
281       {
282         DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG (tag));
283         continue;
284       }
285       success = success && _subset_table (plan, tag);
286     }
287     offset += count;
288   } while (success && count == ARRAY_LENGTH (table_tags));
289
290   hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty ();
291   hb_subset_plan_destroy (plan);
292   return result;
293 }