hb-face-builder.cc (8285B)
1 /* 2 * Copyright © 2009 Red Hat, Inc. 3 * Copyright © 2012 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #include "hb.hh" 30 31 #include "hb-face.hh" 32 33 #include "hb-map.hh" 34 #include "hb-open-file.hh" 35 #include "hb-serialize.hh" 36 37 38 /* 39 * face-builder: A face that has add_table(). 40 */ 41 42 struct face_table_info_t 43 { 44 hb_blob_t* data; 45 unsigned order; 46 }; 47 48 struct hb_face_builder_data_t 49 { 50 hb_hashmap_t<hb_tag_t, face_table_info_t> tables; 51 }; 52 53 static int compare_entries (const void* pa, const void* pb) 54 { 55 const auto& a = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pa; 56 const auto& b = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pb; 57 58 /* Order by blob size first (smallest to largest) and then table tag */ 59 60 if (a.second.order != b.second.order) 61 return a.second.order < b.second.order ? -1 : +1; 62 63 if (a.second.data->length != b.second.data->length) 64 return a.second.data->length < b.second.data->length ? -1 : +1; 65 66 return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; 67 } 68 69 static hb_face_builder_data_t * 70 _hb_face_builder_data_create () 71 { 72 hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); 73 if (unlikely (!data)) 74 return nullptr; 75 76 data->tables.init (); 77 78 return data; 79 } 80 81 static void 82 _hb_face_builder_data_destroy (void *user_data) 83 { 84 hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; 85 86 for (auto info : data->tables.values()) 87 hb_blob_destroy (info.data); 88 89 data->tables.fini (); 90 91 hb_free (data); 92 } 93 94 static hb_blob_t * 95 _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) 96 { 97 98 unsigned int table_count = data->tables.get_population (); 99 unsigned int face_length = table_count * 16 + 12; 100 101 for (auto info : data->tables.values()) 102 face_length += hb_ceil_to_4 (hb_blob_get_length (info.data)); 103 104 char *buf = (char *) hb_malloc (face_length); 105 if (unlikely (!buf)) 106 return nullptr; 107 108 hb_serialize_context_t c (buf, face_length); 109 c.propagate_error (data->tables); 110 OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> (); 111 112 bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) 113 || data->tables.has (HB_TAG ('C','F','F','2'))); 114 hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; 115 116 // Sort the tags so that produced face is deterministic. 117 hb_vector_t<hb_pair_t <hb_tag_t, face_table_info_t>> sorted_entries; 118 data->tables.iter () | hb_sink (sorted_entries); 119 if (unlikely (sorted_entries.in_error ())) 120 { 121 hb_free (buf); 122 return nullptr; 123 } 124 125 sorted_entries.qsort (compare_entries); 126 127 bool ret = f->serialize_single (&c, 128 sfnt_tag, 129 + sorted_entries.iter() 130 | hb_map ([&] (hb_pair_t<hb_tag_t, face_table_info_t> _) { 131 return hb_pair_t<hb_tag_t, hb_blob_t*> (_.first, _.second.data); 132 })); 133 134 c.end_serialize (); 135 136 if (unlikely (!ret)) 137 { 138 hb_free (buf); 139 return nullptr; 140 } 141 142 return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); 143 } 144 145 static hb_blob_t * 146 _hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) 147 { 148 hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; 149 150 if (!tag) 151 return _hb_face_builder_data_reference_blob (data); 152 153 return hb_blob_reference (data->tables[tag].data); 154 } 155 156 static unsigned 157 _hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED, 158 unsigned int start_offset, 159 unsigned int *table_count, 160 hb_tag_t *table_tags, 161 void *user_data) 162 { 163 hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; 164 165 unsigned population = data->tables.get_population (); 166 167 if (!table_count) 168 return population; 169 170 if (unlikely (start_offset >= population)) 171 { 172 *table_count = 0; 173 return population; 174 } 175 176 // Sort the tags. 177 hb_vector_t<hb_tag_t> sorted_tags; 178 data->tables.keys () | hb_sink (sorted_tags); 179 if (unlikely (sorted_tags.in_error ())) 180 { 181 // Not much to do... 182 } 183 sorted_tags.qsort ([] (const void* a, const void* b) { 184 return * (hb_tag_t *) a < * (hb_tag_t *) b ? -1 : 185 * (hb_tag_t *) a == * (hb_tag_t *) b ? 0 : 186 +1; 187 }); 188 189 auto array = sorted_tags.as_array ().sub_array (start_offset, table_count); 190 auto out = hb_array (table_tags, *table_count); 191 192 + array.iter () 193 | hb_sink (out) 194 ; 195 196 return population; 197 } 198 199 200 /** 201 * hb_face_builder_create: 202 * 203 * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). 204 * After tables are added to the face, it can be compiled to a binary 205 * font file by calling hb_face_reference_blob(). 206 * 207 * Return value: (transfer full): New face. 208 * 209 * Since: 1.9.0 210 **/ 211 hb_face_t * 212 hb_face_builder_create () 213 { 214 hb_face_builder_data_t *data = _hb_face_builder_data_create (); 215 if (unlikely (!data)) return hb_face_get_empty (); 216 217 hb_face_t *face = hb_face_create_for_tables (_hb_face_builder_reference_table, 218 data, 219 _hb_face_builder_data_destroy); 220 221 hb_face_set_get_table_tags_func (face, 222 _hb_face_builder_get_table_tags, 223 data, 224 nullptr); 225 226 return face; 227 } 228 229 /** 230 * hb_face_builder_add_table: 231 * @face: A face object created with hb_face_builder_create() 232 * @tag: The #hb_tag_t of the table to add 233 * @blob: The blob containing the table data to add 234 * 235 * Add table for @tag with data provided by @blob to the face. @face must 236 * be created using hb_face_builder_create(). 237 * 238 * Since: 1.9.0 239 **/ 240 hb_bool_t 241 hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) 242 { 243 if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) 244 return false; 245 246 if (tag == HB_MAP_VALUE_INVALID) 247 return false; 248 249 hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; 250 251 hb_blob_t* previous = data->tables.get (tag).data; 252 if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), (unsigned) -1})) 253 { 254 hb_blob_destroy (blob); 255 return false; 256 } 257 258 hb_blob_destroy (previous); 259 return true; 260 } 261 262 /** 263 * hb_face_builder_sort_tables: 264 * @face: A face object created with hb_face_builder_create() 265 * @tags: (array zero-terminated=1): ordered list of table tags terminated by 266 * %HB_TAG_NONE 267 * 268 * Set the ordering of tables for serialization. Any tables not 269 * specified in the tags list will be ordered after the tables in 270 * tags, ordered by the default sort ordering. 271 * 272 * Since: 5.3.0 273 **/ 274 void 275 hb_face_builder_sort_tables (hb_face_t *face, 276 const hb_tag_t *tags) 277 { 278 if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) 279 return; 280 281 hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; 282 283 // Sort all unspecified tables after any specified tables. 284 for (auto& info : data->tables.values_ref()) 285 info.order = (unsigned) -1; 286 287 signed order = 0; 288 for (const hb_tag_t* tag = tags; 289 *tag; 290 tag++) 291 { 292 face_table_info_t* info; 293 if (!data->tables.has (*tag, &info)) continue; 294 info->order = order++; 295 } 296 }