1 /* 2 * Copyright 2011,2014 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): Behdad Esfahbod, Roozbeh Pournader 25 */ 26 27 #include "hb-private.hh" 28 29 #include "hb-ot.h" 30 31 #include "hb-font-private.hh" 32 33 #include "hb-ot-cmap-table.hh" 34 #include "hb-ot-cbdt-table.hh" 35 #include "hb-ot-glyf-table.hh" 36 #include "hb-ot-head-table.hh" 37 #include "hb-ot-hhea-table.hh" 38 #include "hb-ot-hmtx-table.hh" 39 #include "hb-ot-os2-table.hh" 40 #include "hb-ot-var-hvar-table.hh" 41 //#include "hb-ot-post-table.hh" 42 43 44 struct hb_ot_face_metrics_accelerator_t 45 { 46 unsigned int num_metrics; 47 unsigned int num_advances; 48 unsigned int default_advance; 49 unsigned short ascender; 50 unsigned short descender; 51 unsigned short line_gap; 52 bool has_font_extents; 53 54 const OT::hmtxvmtx *table; 55 hb_blob_t *blob; 56 57 const OT::HVARVVAR *var; 58 hb_blob_t *var_blob; 59 60 inline void init (hb_face_t *face, 61 hb_tag_t _hea_tag, 62 hb_tag_t _mtx_tag, 63 hb_tag_t _var_tag, 64 hb_tag_t os2_tag, 65 unsigned int default_advance = 0) 66 { 67 this->default_advance = default_advance ? default_advance : face->get_upem (); 68 69 bool got_font_extents = false; 70 if (os2_tag) 71 { 72 hb_blob_t *os2_blob = OT::Sanitizer<OT::os2>::sanitize (face->reference_table (os2_tag)); 73 const OT::os2 *os2 = OT::Sanitizer<OT::os2>::lock_instance (os2_blob); 74 #define USE_TYPO_METRICS (1u<<7) 75 if (0 != (os2->fsSelection & USE_TYPO_METRICS)) 76 { 77 this->ascender = os2->sTypoAscender; 78 this->descender = os2->sTypoDescender; 79 this->line_gap = os2->sTypoLineGap; 80 got_font_extents = (this->ascender | this->descender) != 0; 81 } 82 hb_blob_destroy (os2_blob); 83 } 84 85 hb_blob_t *_hea_blob = OT::Sanitizer<OT::_hea>::sanitize (face->reference_table (_hea_tag)); 86 const OT::_hea *_hea = OT::Sanitizer<OT::_hea>::lock_instance (_hea_blob); 87 this->num_advances = _hea->numberOfLongMetrics; 88 if (!got_font_extents) 89 { 90 this->ascender = _hea->ascender; 91 this->descender = _hea->descender; 92 this->line_gap = _hea->lineGap; 93 got_font_extents = (this->ascender | this->descender) != 0; 94 } 95 hb_blob_destroy (_hea_blob); 96 97 this->has_font_extents = got_font_extents; 98 99 this->blob = OT::Sanitizer<OT::hmtxvmtx>::sanitize (face->reference_table (_mtx_tag)); 100 101 /* Cap num_metrics() and num_advances() based on table length. */ 102 unsigned int len = hb_blob_get_length (this->blob); 103 if (unlikely (this->num_advances * 4 > len)) 104 this->num_advances = len / 4; 105 this->num_metrics = this->num_advances + (len - 4 * this->num_advances) / 2; 106 107 /* We MUST set num_metrics to zero if num_advances is zero. 108 * Our get_advance() depends on that. */ 109 if (unlikely (!this->num_advances)) 110 { 111 this->num_metrics = this->num_advances = 0; 112 hb_blob_destroy (this->blob); 113 this->blob = hb_blob_get_empty (); 114 } 115 this->table = OT::Sanitizer<OT::hmtxvmtx>::lock_instance (this->blob); 116 117 this->var_blob = OT::Sanitizer<OT::HVARVVAR>::sanitize (face->reference_table (_var_tag)); 118 this->var = OT::Sanitizer<OT::HVARVVAR>::lock_instance (this->var_blob); 119 } 120 121 inline void fini (void) 122 { 123 hb_blob_destroy (this->blob); 124 hb_blob_destroy (this->var_blob); 125 } 126 127 inline unsigned int get_advance (hb_codepoint_t glyph, 128 hb_font_t *font) const 129 { 130 if (unlikely (glyph >= this->num_metrics)) 131 { 132 /* If this->num_metrics is zero, it means we don't have the metrics table 133 * for this direction: return default advance. Otherwise, it means that the 134 * glyph index is out of bound: return zero. */ 135 if (this->num_metrics) 136 return 0; 137 else 138 return this->default_advance; 139 } 140 141 return this->table->longMetric[MIN (glyph, this->num_advances - 1)].advance 142 + this->var->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?! 143 } 144 }; 145 146 struct hb_ot_face_glyf_accelerator_t 147 { 148 bool short_offset; 149 unsigned int num_glyphs; 150 const OT::loca *loca; 151 const OT::glyf *glyf; 152 hb_blob_t *loca_blob; 153 hb_blob_t *glyf_blob; 154 unsigned int glyf_len; 155 156 inline void init (hb_face_t *face) 157 { 158 hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (face->reference_table (HB_OT_TAG_head)); 159 const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob); 160 if ((unsigned int) head->indexToLocFormat > 1 || head->glyphDataFormat != 0) 161 { 162 /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ 163 hb_blob_destroy (head_blob); 164 return; 165 } 166 this->short_offset = 0 == head->indexToLocFormat; 167 hb_blob_destroy (head_blob); 168 169 this->loca_blob = OT::Sanitizer<OT::loca>::sanitize (face->reference_table (HB_OT_TAG_loca)); 170 this->loca = OT::Sanitizer<OT::loca>::lock_instance (this->loca_blob); 171 this->glyf_blob = OT::Sanitizer<OT::glyf>::sanitize (face->reference_table (HB_OT_TAG_glyf)); 172 this->glyf = OT::Sanitizer<OT::glyf>::lock_instance (this->glyf_blob); 173 174 this->num_glyphs = MAX (1u, hb_blob_get_length (this->loca_blob) / (this->short_offset ? 2 : 4)) - 1; 175 this->glyf_len = hb_blob_get_length (this->glyf_blob); 176 } 177 178 inline void fini (void) 179 { 180 hb_blob_destroy (this->loca_blob); 181 hb_blob_destroy (this->glyf_blob); 182 } 183 184 inline bool get_extents (hb_codepoint_t glyph, 185 hb_glyph_extents_t *extents) const 186 { 187 if (unlikely (glyph >= this->num_glyphs)) 188 return false; 189 190 unsigned int start_offset, end_offset; 191 if (this->short_offset) 192 { 193 start_offset = 2 * this->loca->u.shortsZ[glyph]; 194 end_offset = 2 * this->loca->u.shortsZ[glyph + 1]; 195 } 196 else 197 { 198 start_offset = this->loca->u.longsZ[glyph]; 199 end_offset = this->loca->u.longsZ[glyph + 1]; 200 } 201 202 if (start_offset > end_offset || end_offset > this->glyf_len) 203 return false; 204 205 if (end_offset - start_offset < OT::glyfGlyphHeader::static_size) 206 return true; /* Empty glyph; zero extents. */ 207 208 const OT::glyfGlyphHeader &glyph_header = OT::StructAtOffset<OT::glyfGlyphHeader> (this->glyf, start_offset); 209 210 extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); 211 extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); 212 extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; 213 extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; 214 215 return true; 216 } 217 }; 218 219 struct hb_ot_face_cbdt_accelerator_t 220 { 221 hb_blob_t *cblc_blob; 222 hb_blob_t *cbdt_blob; 223 const OT::CBLC *cblc; 224 const OT::CBDT *cbdt; 225 226 unsigned int cbdt_len; 227 float upem; 228 229 inline void init (hb_face_t *face) 230 { 231 upem = face->get_upem(); 232 233 cblc_blob = OT::Sanitizer<OT::CBLC>::sanitize (face->reference_table (HB_OT_TAG_CBLC)); 234 cbdt_blob = OT::Sanitizer<OT::CBDT>::sanitize (face->reference_table (HB_OT_TAG_CBDT)); 235 cbdt_len = hb_blob_get_length (cbdt_blob); 236 237 if (hb_blob_get_length (cblc_blob) == 0) { 238 cblc = NULL; 239 cbdt = NULL; 240 return; /* Not a bitmap font. */ 241 } 242 cblc = OT::Sanitizer<OT::CBLC>::lock_instance (cblc_blob); 243 cbdt = OT::Sanitizer<OT::CBDT>::lock_instance (cbdt_blob); 244 245 } 246 247 inline void fini (void) 248 { 249 hb_blob_destroy (this->cblc_blob); 250 hb_blob_destroy (this->cbdt_blob); 251 } 252 253 inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const 254 { 255 unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */ 256 257 if (cblc == NULL) 258 return false; // Not a color bitmap font. 259 260 const OT::IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem); 261 if (subtable_record == NULL) 262 return false; 263 264 if (subtable_record->get_extents (extents)) 265 return true; 266 267 unsigned int image_offset = 0, image_length = 0, image_format = 0; 268 if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format)) 269 return false; 270 271 { 272 /* TODO Move the following into CBDT struct when adding more formats. */ 273 274 if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) 275 return false; 276 277 switch (image_format) 278 { 279 case 17: { 280 if (unlikely (image_length < OT::GlyphBitmapDataFormat17::min_size)) 281 return false; 282 283 const OT::GlyphBitmapDataFormat17& glyphFormat17 = 284 OT::StructAtOffset<OT::GlyphBitmapDataFormat17> (this->cbdt, image_offset); 285 glyphFormat17.glyphMetrics.get_extents (extents); 286 } 287 break; 288 default: 289 // TODO: Support other image formats. 290 return false; 291 } 292 } 293 294 /* Convert to the font units. */ 295 extents->x_bearing *= upem / (float) x_ppem; 296 extents->y_bearing *= upem / (float) y_ppem; 297 extents->width *= upem / (float) x_ppem; 298 extents->height *= upem / (float) y_ppem; 299 300 return true; 301 } 302 }; 303 304 typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, 305 hb_codepoint_t codepoint, 306 hb_codepoint_t *glyph); 307 308 template <typename Type> 309 static inline bool get_glyph_from (const void *obj, 310 hb_codepoint_t codepoint, 311 hb_codepoint_t *glyph) 312 { 313 const Type *typed_obj = (const Type *) obj; 314 return typed_obj->get_glyph (codepoint, glyph); 315 } 316 317 template <typename Type> 318 static inline bool get_glyph_from_symbol (const void *obj, 319 hb_codepoint_t codepoint, 320 hb_codepoint_t *glyph) 321 { 322 const Type *typed_obj = (const Type *) obj; 323 if (likely (typed_obj->get_glyph (codepoint, glyph))) 324 return true; 325 326 if (codepoint <= 0x00FFu) 327 { 328 /* For symbol-encoded OpenType fonts, we duplicate the 329 * U+F000..F0FF range at U+0000..U+00FF. That's what 330 * Windows seems to do, and that's hinted about at: 331 * http://www.microsoft.com/typography/otspec/recom.htm 332 * under "Non-Standard (Symbol) Fonts". */ 333 return typed_obj->get_glyph (0xF000u + codepoint, glyph); 334 } 335 336 return false; 337 } 338 339 struct hb_ot_face_cmap_accelerator_t 340 { 341 hb_cmap_get_glyph_func_t get_glyph_func; 342 const void *get_glyph_data; 343 OT::CmapSubtableFormat4::accelerator_t format4_accel; 344 345 const OT::CmapSubtableFormat14 *uvs_table; 346 hb_blob_t *blob; 347 348 inline void init (hb_face_t *face) 349 { 350 this->blob = OT::Sanitizer<OT::cmap>::sanitize (face->reference_table (HB_OT_TAG_cmap)); 351 const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob); 352 const OT::CmapSubtable *subtable = NULL; 353 const OT::CmapSubtableFormat14 *subtable_uvs = NULL; 354 355 bool symbol = false; 356 /* 32-bit subtables. */ 357 if (!subtable) subtable = cmap->find_subtable (3, 10); 358 if (!subtable) subtable = cmap->find_subtable (0, 6); 359 if (!subtable) subtable = cmap->find_subtable (0, 4); 360 /* 16-bit subtables. */ 361 if (!subtable) subtable = cmap->find_subtable (3, 1); 362 if (!subtable) subtable = cmap->find_subtable (0, 3); 363 if (!subtable) subtable = cmap->find_subtable (0, 2); 364 if (!subtable) subtable = cmap->find_subtable (0, 1); 365 if (!subtable) subtable = cmap->find_subtable (0, 0); 366 if (!subtable) 367 { 368 subtable = cmap->find_subtable (3, 0); 369 if (subtable) symbol = true; 370 } 371 /* Meh. */ 372 if (!subtable) subtable = &OT::Null(OT::CmapSubtable); 373 374 /* UVS subtable. */ 375 if (!subtable_uvs) 376 { 377 const OT::CmapSubtable *st = cmap->find_subtable (0, 5); 378 if (st && st->u.format == 14) 379 subtable_uvs = &st->u.format14; 380 } 381 /* Meh. */ 382 if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); 383 384 this->uvs_table = subtable_uvs; 385 386 this->get_glyph_data = subtable; 387 if (unlikely (symbol)) 388 this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>; 389 else 390 switch (subtable->u.format) { 391 /* Accelerate format 4 and format 12. */ 392 default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>; break; 393 case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>; break; 394 case 4: 395 { 396 this->format4_accel.init (&subtable->u.format4); 397 this->get_glyph_data = &this->format4_accel; 398 this->get_glyph_func = this->format4_accel.get_glyph_func; 399 } 400 break; 401 } 402 } 403 404 inline void fini (void) 405 { 406 hb_blob_destroy (this->blob); 407 } 408 409 inline bool get_nominal_glyph (hb_codepoint_t unicode, 410 hb_codepoint_t *glyph) const 411 { 412 return this->get_glyph_func (this->get_glyph_data, unicode, glyph); 413 } 414 415 inline bool get_variation_glyph (hb_codepoint_t unicode, 416 hb_codepoint_t variation_selector, 417 hb_codepoint_t *glyph) const 418 { 419 switch (this->uvs_table->get_glyph_variant (unicode, 420 variation_selector, 421 glyph)) 422 { 423 case OT::GLYPH_VARIANT_NOT_FOUND: return false; 424 case OT::GLYPH_VARIANT_FOUND: return true; 425 case OT::GLYPH_VARIANT_USE_DEFAULT: break; 426 } 427 428 return get_nominal_glyph (unicode, glyph); 429 } 430 }; 431 432 struct hb_ot_font_t 433 { 434 hb_ot_face_cmap_accelerator_t cmap; 435 hb_ot_face_metrics_accelerator_t h_metrics; 436 hb_ot_face_metrics_accelerator_t v_metrics; 437 OT::hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf; 438 OT::hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt; 439 }; 440 441 442 static hb_ot_font_t * 443 _hb_ot_font_create (hb_face_t *face) 444 { 445 hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); 446 447 if (unlikely (!ot_font)) 448 return NULL; 449 450 ot_font->cmap.init (face); 451 ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_HVAR, HB_OT_TAG_os2); 452 ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_OT_TAG_VVAR, HB_TAG_NONE, 453 ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */ 454 ot_font->glyf.init (face); 455 ot_font->cbdt.init (face); 456 457 return ot_font; 458 } 459 460 static void 461 _hb_ot_font_destroy (hb_ot_font_t *ot_font) 462 { 463 ot_font->cmap.fini (); 464 ot_font->h_metrics.fini (); 465 ot_font->v_metrics.fini (); 466 ot_font->glyf.fini (); 467 ot_font->cbdt.fini (); 468 469 free (ot_font); 470 } 471 472 473 static hb_bool_t 474 hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, 475 void *font_data, 476 hb_codepoint_t unicode, 477 hb_codepoint_t *glyph, 478 void *user_data HB_UNUSED) 479 480 { 481 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 482 return ot_font->cmap.get_nominal_glyph (unicode, glyph); 483 } 484 485 static hb_bool_t 486 hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, 487 void *font_data, 488 hb_codepoint_t unicode, 489 hb_codepoint_t variation_selector, 490 hb_codepoint_t *glyph, 491 void *user_data HB_UNUSED) 492 { 493 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 494 return ot_font->cmap.get_variation_glyph (unicode, variation_selector, glyph); 495 } 496 497 static hb_position_t 498 hb_ot_get_glyph_h_advance (hb_font_t *font, 499 void *font_data, 500 hb_codepoint_t glyph, 501 void *user_data HB_UNUSED) 502 { 503 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 504 return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font)); 505 } 506 507 static hb_position_t 508 hb_ot_get_glyph_v_advance (hb_font_t *font, 509 void *font_data, 510 hb_codepoint_t glyph, 511 void *user_data HB_UNUSED) 512 { 513 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 514 return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font)); 515 } 516 517 static hb_bool_t 518 hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, 519 void *font_data, 520 hb_codepoint_t glyph, 521 hb_glyph_extents_t *extents, 522 void *user_data HB_UNUSED) 523 { 524 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 525 bool ret = ot_font->glyf->get_extents (glyph, extents); 526 if (!ret) 527 ret = ot_font->cbdt->get_extents (glyph, extents); 528 // TODO Hook up side-bearings variations. 529 extents->x_bearing = font->em_scale_x (extents->x_bearing); 530 extents->y_bearing = font->em_scale_y (extents->y_bearing); 531 extents->width = font->em_scale_x (extents->width); 532 extents->height = font->em_scale_y (extents->height); 533 return ret; 534 } 535 536 static hb_bool_t 537 hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED, 538 void *font_data, 539 hb_font_extents_t *metrics, 540 void *user_data HB_UNUSED) 541 { 542 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 543 metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender); 544 metrics->descender = font->em_scale_y (ot_font->h_metrics.descender); 545 metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap); 546 // TODO Hook up variations. 547 return ot_font->h_metrics.has_font_extents; 548 } 549 550 static hb_bool_t 551 hb_ot_get_font_v_extents (hb_font_t *font HB_UNUSED, 552 void *font_data, 553 hb_font_extents_t *metrics, 554 void *user_data HB_UNUSED) 555 { 556 const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; 557 metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender); 558 metrics->descender = font->em_scale_x (ot_font->v_metrics.descender); 559 metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap); 560 // TODO Hook up variations. 561 return ot_font->v_metrics.has_font_extents; 562 } 563 564 static hb_font_funcs_t *static_ot_funcs = NULL; 565 566 #ifdef HB_USE_ATEXIT 567 static 568 void free_static_ot_funcs (void) 569 { 570 hb_font_funcs_destroy (static_ot_funcs); 571 } 572 #endif 573 574 static hb_font_funcs_t * 575 _hb_ot_get_font_funcs (void) 576 { 577 retry: 578 hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ot_funcs); 579 580 if (unlikely (!funcs)) 581 { 582 funcs = hb_font_funcs_create (); 583 584 hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, NULL, NULL); 585 hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, NULL, NULL); 586 hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, NULL, NULL); 587 hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, NULL, NULL); 588 hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, NULL, NULL); 589 hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, NULL, NULL); 590 //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, NULL, NULL); 591 //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, NULL, NULL); 592 //hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, NULL, NULL); TODO 593 //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, NULL, NULL); 594 hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, NULL, NULL); 595 //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, NULL, NULL); TODO 596 //hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, NULL, NULL); TODO 597 //hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, NULL, NULL); TODO 598 599 hb_font_funcs_make_immutable (funcs); 600 601 if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, NULL, funcs)) { 602 hb_font_funcs_destroy (funcs); 603 goto retry; 604 } 605 606 #ifdef HB_USE_ATEXIT 607 atexit (free_static_ot_funcs); /* First person registers atexit() callback. */ 608 #endif 609 }; 610 611 return funcs; 612 } 613 614 615 /** 616 * hb_ot_font_set_funcs: 617 * 618 * Since: 0.9.28 619 **/ 620 void 621 hb_ot_font_set_funcs (hb_font_t *font) 622 { 623 hb_ot_font_t *ot_font = _hb_ot_font_create (font->face); 624 if (unlikely (!ot_font)) 625 return; 626 627 hb_font_set_funcs (font, 628 _hb_ot_get_font_funcs (), 629 ot_font, 630 (hb_destroy_func_t) _hb_ot_font_destroy); 631 } 632