|
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 } |