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