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 }