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