OLD | NEW |
1 /* | 1 /* |
2 * Copyright © 2012,2013 Mozilla Foundation. | 2 * Copyright © 2012,2013 Mozilla Foundation. |
3 * Copyright © 2012,2013 Google, Inc. | 3 * Copyright © 2012,2013 Google, Inc. |
4 * | 4 * |
5 * This is part of HarfBuzz, a text shaping library. | 5 * This is part of HarfBuzz, a text shaping library. |
6 * | 6 * |
7 * Permission is hereby granted, without written agreement and without | 7 * Permission is hereby granted, without written agreement and without |
8 * license or royalty fees, to use, copy, modify, and distribute this | 8 * license or royalty fees, to use, copy, modify, and distribute this |
9 * software and its documentation for any purpose, provided that the | 9 * software and its documentation for any purpose, provided that the |
10 * above copyright notice and the following two paragraphs appear in | 10 * above copyright notice and the following two paragraphs appear in |
11 * all copies of this software. | 11 * all copies of this software. |
12 * | 12 * |
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 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 | 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
17 * DAMAGE. | 17 * DAMAGE. |
18 * | 18 * |
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | 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 | 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
24 * | 24 * |
25 * Mozilla Author(s): Jonathan Kew | 25 * Mozilla Author(s): Jonathan Kew |
26 * Google Author(s): Behdad Esfahbod | 26 * Google Author(s): Behdad Esfahbod |
27 */ | 27 */ |
28 | 28 |
29 #define HB_SHAPER coretext | 29 #define HB_SHAPER coretext |
| 30 #define hb_coretext_shaper_face_data_t CGFont |
30 #include "hb-shaper-impl-private.hh" | 31 #include "hb-shaper-impl-private.hh" |
31 | 32 |
32 #include "hb-coretext.h" | 33 #include "hb-coretext.h" |
33 | 34 |
34 | 35 |
35 #ifndef HB_DEBUG_CORETEXT | 36 #ifndef HB_DEBUG_CORETEXT |
36 #define HB_DEBUG_CORETEXT (HB_DEBUG+0) | 37 #define HB_DEBUG_CORETEXT (HB_DEBUG+0) |
37 #endif | 38 #endif |
38 | 39 |
39 | 40 |
(...skipping 30 matching lines...) Expand all Loading... |
70 | 71 |
71 | 72 |
72 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) | 73 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) |
73 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) | 74 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) |
74 | 75 |
75 | 76 |
76 /* | 77 /* |
77 * shaper face data | 78 * shaper face data |
78 */ | 79 */ |
79 | 80 |
80 struct hb_coretext_shaper_face_data_t { | |
81 CGFontRef cg_font; | |
82 }; | |
83 | |
84 static void | 81 static void |
85 release_data (void *info, const void *data, size_t size) | 82 release_data (void *info, const void *data, size_t size) |
86 { | 83 { |
87 assert (hb_blob_get_length ((hb_blob_t *) info) == size && | 84 assert (hb_blob_get_length ((hb_blob_t *) info) == size && |
88 hb_blob_get_data ((hb_blob_t *) info, NULL) == data); | 85 hb_blob_get_data ((hb_blob_t *) info, NULL) == data); |
89 | 86 |
90 hb_blob_destroy ((hb_blob_t *) info); | 87 hb_blob_destroy ((hb_blob_t *) info); |
91 } | 88 } |
92 | 89 |
93 hb_coretext_shaper_face_data_t * | 90 hb_coretext_shaper_face_data_t * |
94 _hb_coretext_shaper_face_data_create (hb_face_t *face) | 91 _hb_coretext_shaper_face_data_create (hb_face_t *face) |
95 { | 92 { |
96 hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) call
oc (1, sizeof (hb_coretext_shaper_face_data_t)); | 93 hb_coretext_shaper_face_data_t *data = NULL; |
97 if (unlikely (!data)) | |
98 return NULL; | |
99 | 94 |
100 if (face->destroy == (hb_destroy_func_t) CGFontRelease) | 95 if (face->destroy == (hb_destroy_func_t) CGFontRelease) |
101 { | 96 { |
102 data->cg_font = CGFontRetain ((CGFontRef) face->user_data); | 97 data = CGFontRetain ((CGFontRef) face->user_data); |
103 } | 98 } |
104 else | 99 else |
105 { | 100 { |
106 hb_blob_t *blob = hb_face_reference_blob (face); | 101 hb_blob_t *blob = hb_face_reference_blob (face); |
107 unsigned int blob_length; | 102 unsigned int blob_length; |
108 const char *blob_data = hb_blob_get_data (blob, &blob_length); | 103 const char *blob_data = hb_blob_get_data (blob, &blob_length); |
109 if (unlikely (!blob_length)) | 104 if (unlikely (!blob_length)) |
110 DEBUG_MSG (CORETEXT, face, "Face has empty blob"); | 105 DEBUG_MSG (CORETEXT, face, "Face has empty blob"); |
111 | 106 |
112 CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data,
blob_length, &release_data); | 107 CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data,
blob_length, &release_data); |
113 data->cg_font = CGFontCreateWithDataProvider (provider); | 108 if (likely (provider)) |
114 CGDataProviderRelease (provider); | 109 { |
| 110 data = CGFontCreateWithDataProvider (provider); |
| 111 CGDataProviderRelease (provider); |
| 112 } |
115 } | 113 } |
116 | 114 |
117 if (unlikely (!data->cg_font)) { | 115 if (unlikely (!data)) { |
118 DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); | 116 DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); |
119 free (data); | |
120 return NULL; | |
121 } | 117 } |
122 | 118 |
123 return data; | 119 return data; |
124 } | 120 } |
125 | 121 |
126 void | 122 void |
127 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) | 123 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) |
128 { | 124 { |
129 CFRelease (data->cg_font); | 125 CFRelease (data); |
130 free (data); | |
131 } | 126 } |
132 | 127 |
133 CGFontRef | 128 CGFontRef |
134 hb_coretext_face_get_cg_font (hb_face_t *face) | 129 hb_coretext_face_get_cg_font (hb_face_t *face) |
135 { | 130 { |
136 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; | 131 if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; |
137 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); | 132 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
138 return face_data->cg_font; | 133 return face_data; |
139 } | 134 } |
140 | 135 |
141 | 136 |
142 /* | 137 /* |
143 * shaper font data | 138 * shaper font data |
144 */ | 139 */ |
145 | 140 |
146 struct hb_coretext_shaper_font_data_t { | 141 struct hb_coretext_shaper_font_data_t { |
147 CTFontRef ct_font; | 142 CTFontRef ct_font; |
148 }; | 143 }; |
149 | 144 |
150 hb_coretext_shaper_font_data_t * | 145 hb_coretext_shaper_font_data_t * |
151 _hb_coretext_shaper_font_data_create (hb_font_t *font) | 146 _hb_coretext_shaper_font_data_create (hb_font_t *font) |
152 { | 147 { |
153 if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; | 148 if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; |
154 | 149 |
155 hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) call
oc (1, sizeof (hb_coretext_shaper_font_data_t)); | 150 hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) call
oc (1, sizeof (hb_coretext_shaper_font_data_t)); |
156 if (unlikely (!data)) | 151 if (unlikely (!data)) |
157 return NULL; | 152 return NULL; |
158 | 153 |
159 hb_face_t *face = font->face; | 154 hb_face_t *face = font->face; |
160 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); | 155 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
161 | 156 |
162 data->ct_font = CTFontCreateWithGraphicsFont (face_data->cg_font, font->y_scal
e, NULL, NULL); | 157 data->ct_font = CTFontCreateWithGraphicsFont (face_data, font->y_scale, NULL,
NULL); |
163 if (unlikely (!data->ct_font)) { | 158 if (unlikely (!data->ct_font)) { |
164 DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); | 159 DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); |
165 free (data); | 160 free (data); |
166 return NULL; | 161 return NULL; |
167 } | 162 } |
168 | 163 |
169 return data; | 164 return data; |
170 } | 165 } |
171 | 166 |
172 void | 167 void |
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 _hb_coretext_shape (hb_shape_plan_t *shape_plan, | 424 _hb_coretext_shape (hb_shape_plan_t *shape_plan, |
430 hb_font_t *font, | 425 hb_font_t *font, |
431 hb_buffer_t *buffer, | 426 hb_buffer_t *buffer, |
432 const hb_feature_t *features, | 427 const hb_feature_t *features, |
433 unsigned int num_features) | 428 unsigned int num_features) |
434 { | 429 { |
435 hb_face_t *face = font->face; | 430 hb_face_t *face = font->face; |
436 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); | 431 hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
437 hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); | 432 hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); |
438 | 433 |
| 434 /* Attach marks to their bases, to match the 'ot' shaper. |
| 435 * Adapted from hb-ot-shape:hb_form_clusters(). |
| 436 * Note that this only makes us be closer to the 'ot' shaper, |
| 437 * but by no means the same. For example, if there's |
| 438 * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will |
| 439 * continue pointing to B2 even though B2 was merged into B1's |
| 440 * cluster... */ |
| 441 { |
| 442 hb_unicode_funcs_t *unicode = buffer->unicode; |
| 443 unsigned int count = buffer->len; |
| 444 hb_glyph_info_t *info = buffer->info; |
| 445 for (unsigned int i = 1; i < count; i++) |
| 446 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i
].codepoint))) |
| 447 buffer->merge_clusters (i - 1, i + 1); |
| 448 } |
| 449 |
| 450 hb_auto_array_t<feature_record_t> feature_records; |
| 451 hb_auto_array_t<range_record_t> range_records; |
| 452 |
439 /* | 453 /* |
440 * Set up features. | 454 * Set up features. |
441 * (copied + modified from code from hb-uniscribe.cc) | 455 * (copied + modified from code from hb-uniscribe.cc) |
442 */ | 456 */ |
443 hb_auto_array_t<feature_record_t> feature_records; | |
444 hb_auto_array_t<range_record_t> range_records; | |
445 if (num_features) | 457 if (num_features) |
446 { | 458 { |
447 /* Sort features by start/end events. */ | 459 /* Sort features by start/end events. */ |
448 hb_auto_array_t<feature_event_t> feature_events; | 460 hb_auto_array_t<feature_event_t> feature_events; |
449 for (unsigned int i = 0; i < num_features; i++) | 461 for (unsigned int i = 0; i < num_features; i++) |
450 { | 462 { |
451 const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&
features[i].tag, | 463 const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&
features[i].tag, |
452 f
eature_mappings, | 464 f
eature_mappings, |
453 A
RRAY_LENGTH (feature_mappings), | 465 A
RRAY_LENGTH (feature_mappings), |
454 s
izeof (feature_mappings[0]), | 466 s
izeof (feature_mappings[0]), |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
542 (const void **) &feat
ures_array, | 554 (const void **) &feat
ures_array, |
543 1, | 555 1, |
544 &kCFTypeDictionaryKey
CallBacks, | 556 &kCFTypeDictionaryKey
CallBacks, |
545 &kCFTypeDictionaryVal
ueCallBacks); | 557 &kCFTypeDictionaryVal
ueCallBacks); |
546 CFRelease (features_array); | 558 CFRelease (features_array); |
547 | 559 |
548 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (
attributes); | 560 CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (
attributes); |
549 CFRelease (attributes); | 561 CFRelease (attributes); |
550 | 562 |
551 range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0,
NULL, font_desc); | 563 range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0,
NULL, font_desc); |
552 | |
553 CFRelease (font_desc); | 564 CFRelease (font_desc); |
554 } | 565 } |
555 else | 566 else |
556 { | 567 { |
557 range->font = NULL; | 568 range->font = NULL; |
558 } | 569 } |
559 | 570 |
560 range->index_first = last_index; | 571 range->index_first = last_index; |
561 range->index_last = event->index - 1; | 572 range->index_last = event->index - 1; |
562 | 573 |
(...skipping 14 matching lines...) Expand all Loading... |
577 | 588 |
578 if (!range_records.len) /* No active feature found. */ | 589 if (!range_records.len) /* No active feature found. */ |
579 goto fail_features; | 590 goto fail_features; |
580 } | 591 } |
581 else | 592 else |
582 { | 593 { |
583 fail_features: | 594 fail_features: |
584 num_features = 0; | 595 num_features = 0; |
585 } | 596 } |
586 | 597 |
587 #define FAIL(...) \ | |
588 HB_STMT_START { \ | |
589 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ | |
590 return false; \ | |
591 } HB_STMT_END; | |
592 | |
593 unsigned int scratch_size; | 598 unsigned int scratch_size; |
594 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_
size); | 599 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_
size); |
595 | 600 |
596 #define ALLOCATE_ARRAY(Type, name, len) \ | 601 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ |
597 Type *name = (Type *) scratch; \ | 602 Type *name = (Type *) scratch; \ |
598 { \ | 603 { \ |
599 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch))
; \ | 604 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch))
; \ |
600 assert (_consumed <= scratch_size); \ | 605 if (unlikely (_consumed > scratch_size)) \ |
| 606 { \ |
| 607 on_no_room; \ |
| 608 assert (0); \ |
| 609 } \ |
601 scratch += _consumed; \ | 610 scratch += _consumed; \ |
602 scratch_size -= _consumed; \ | 611 scratch_size -= _consumed; \ |
603 } | 612 } |
604 | 613 |
605 #define utf16_index() var1.u32 | 614 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); |
606 | |
607 ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2); | |
608 | |
609 unsigned int chars_len = 0; | 615 unsigned int chars_len = 0; |
610 for (unsigned int i = 0; i < buffer->len; i++) { | 616 for (unsigned int i = 0; i < buffer->len; i++) { |
611 hb_codepoint_t c = buffer->info[i].codepoint; | 617 hb_codepoint_t c = buffer->info[i].codepoint; |
612 buffer->info[i].utf16_index() = chars_len; | |
613 if (likely (c <= 0xFFFFu)) | 618 if (likely (c <= 0xFFFFu)) |
614 pchars[chars_len++] = c; | 619 pchars[chars_len++] = c; |
615 else if (unlikely (c > 0x10FFFFu)) | 620 else if (unlikely (c > 0x10FFFFu)) |
616 pchars[chars_len++] = 0xFFFDu; | 621 pchars[chars_len++] = 0xFFFDu; |
617 else { | 622 else { |
618 pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); | 623 pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); |
619 pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); | 624 pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); |
620 } | 625 } |
621 } | 626 } |
622 | 627 |
623 #undef utf16_index | 628 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); |
624 | 629 chars_len = 0; |
625 CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (NULL, | 630 for (unsigned int i = 0; i < buffer->len; i++) |
626 pchars, chars_len
, | |
627 kCFAllocatorNull)
; | |
628 | |
629 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NU
LL, chars_len); | |
630 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); | |
631 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), | |
632 » » » » kCTFontAttributeName, font_data->ct_font); | |
633 | |
634 if (num_features) | |
635 { | 631 { |
636 ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len); | 632 hb_codepoint_t c = buffer->info[i].codepoint; |
637 | 633 unsigned int cluster = buffer->info[i].cluster; |
638 /* Need log_clusters to assign features. */ | 634 log_clusters[chars_len++] = cluster; |
639 chars_len = 0; | 635 if (hb_in_range (c, 0x10000u, 0x10FFFFu)) |
640 for (unsigned int i = 0; i < buffer->len; i++) | 636 log_clusters[chars_len++] = cluster; /* Surrogates. */ |
| 637 } |
| 638 |
| 639 #define FAIL(...) \ |
| 640 HB_STMT_START { \ |
| 641 DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ |
| 642 ret = false; \ |
| 643 goto fail; \ |
| 644 } HB_STMT_END; |
| 645 |
| 646 bool ret = true; |
| 647 CFStringRef string_ref = NULL; |
| 648 CTLineRef line = NULL; |
| 649 |
| 650 if (0) |
| 651 { |
| 652 resize_and_retry: |
| 653 DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); |
| 654 /* string_ref uses the scratch-buffer for backing store, and line references |
| 655 * string_ref (via attr_string). We must release those before resizing buff
er. */ |
| 656 assert (string_ref); |
| 657 assert (line); |
| 658 CFRelease (string_ref); |
| 659 CFRelease (line); |
| 660 string_ref = NULL; |
| 661 line = NULL; |
| 662 |
| 663 /* Get previous start-of-scratch-area, that we use later for readjusting |
| 664 * our existing scratch arrays. */ |
| 665 unsigned int old_scratch_used; |
| 666 hb_buffer_t::scratch_buffer_t *old_scratch; |
| 667 old_scratch = buffer->get_scratch_buffer (&old_scratch_used); |
| 668 old_scratch_used = scratch - old_scratch; |
| 669 |
| 670 if (unlikely (!buffer->ensure (buffer->allocated * 2))) |
| 671 FAIL ("Buffer resize failed"); |
| 672 |
| 673 /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really
the |
| 674 * cleanest way to do without completely restructuring the rest of this shap
er. */ |
| 675 scratch = buffer->get_scratch_buffer (&scratch_size); |
| 676 pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars -
(char *) old_scratch))); |
| 677 log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char
*) log_clusters - (char *) old_scratch))); |
| 678 scratch += old_scratch_used; |
| 679 scratch_size -= old_scratch_used; |
| 680 } |
| 681 retry: |
| 682 { |
| 683 string_ref = CFStringCreateWithCharactersNoCopy (NULL, |
| 684 » » » » » » pchars, chars_len, |
| 685 » » » » » » kCFAllocatorNull); |
| 686 if (unlikely (!string_ref)) |
| 687 FAIL ("CFStringCreateWithCharactersNoCopy failed"); |
| 688 |
| 689 /* Create an attributed string, populate it, and create a line from it, then
release attributed string. */ |
641 { | 690 { |
642 hb_codepoint_t c = buffer->info[i].codepoint; | 691 CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable
(kCFAllocatorDefault, |
643 unsigned int cluster = buffer->info[i].cluster; | 692 » » » » » » » » » »
chars_len); |
644 log_clusters[chars_len++] = cluster; | 693 if (unlikely (!attr_string)) |
645 if (hb_in_range (c, 0x10000u, 0x10FFFFu)) | 694 » FAIL ("CFAttributedStringCreateMutable failed"); |
646 » log_clusters[chars_len++] = cluster; /* Surrogates. */ | 695 CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_r
ef); |
| 696 if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) |
| 697 { |
| 698 » CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
| 699 » » » » » kCTVerticalFormsAttributeName, kCFBoolea
nTrue); |
| 700 } |
| 701 |
| 702 if (buffer->props.language) |
| 703 { |
| 704 /* What's the iOS equivalent of this check? |
| 705 * The symbols was introduced in iOS 7.0. |
| 706 * At any rate, our fallback is safe and works fine. */ |
| 707 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 |
| 708 # define kCTLanguageAttributeName CFSTR ("NSLanguage") |
| 709 #endif |
| 710 CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, |
| 711 » » » » » » » hb_language_to_strin
g (buffer->props.language), |
| 712 » » » » » » » kCFStringEncodingUTF
8, |
| 713 » » » » » » » kCFAllocatorNull); |
| 714 » if (unlikely (!lang)) |
| 715 » FAIL ("CFStringCreateWithCStringNoCopy failed"); |
| 716 » CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
| 717 » » » » » kCTLanguageAttributeName, lang); |
| 718 » CFRelease (lang); |
| 719 } |
| 720 CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), |
| 721 » » » » kCTFontAttributeName, font_data->ct_font); |
| 722 |
| 723 if (num_features) |
| 724 { |
| 725 » unsigned int start = 0; |
| 726 » range_record_t *last_range = &range_records[0]; |
| 727 » for (unsigned int k = 0; k < chars_len; k++) |
| 728 » { |
| 729 » range_record_t *range = last_range; |
| 730 » while (log_clusters[k] < range->index_first) |
| 731 » range--; |
| 732 » while (log_clusters[k] > range->index_last) |
| 733 » range++; |
| 734 » if (range != last_range) |
| 735 » { |
| 736 » if (last_range->font) |
| 737 » CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k
- start), |
| 738 » » » » » kCTFontAttributeName, last_range->
font); |
| 739 |
| 740 » start = k; |
| 741 » } |
| 742 |
| 743 » last_range = range; |
| 744 » } |
| 745 » if (start != chars_len && last_range->font) |
| 746 » CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars
_len - start), |
| 747 » » » » » kCTFontAttributeName, last_range->font
); |
| 748 } |
| 749 |
| 750 int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; |
| 751 CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberI
ntType, &level); |
| 752 CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, |
| 753 » » » » » » (const void **) &kCTTypesett
erOptionForcedEmbeddingLevel, |
| 754 » » » » » » (const void **) &level_numbe
r, |
| 755 » » » » » » 1, |
| 756 » » » » » » &kCFTypeDictionaryKeyCallBac
ks, |
| 757 » » » » » » &kCFTypeDictionaryValueCallB
acks); |
| 758 if (unlikely (!options)) |
| 759 FAIL ("CFDictionaryCreate failed"); |
| 760 |
| 761 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOpti
ons (attr_string, options); |
| 762 CFRelease (options); |
| 763 CFRelease (attr_string); |
| 764 if (unlikely (!typesetter)) |
| 765 » FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); |
| 766 |
| 767 line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); |
| 768 CFRelease (typesetter); |
| 769 if (unlikely (!line)) |
| 770 » FAIL ("CTTypesetterCreateLine failed"); |
647 } | 771 } |
648 | 772 |
649 unsigned int start = 0; | 773 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); |
650 range_record_t *last_range = &range_records[0]; | 774 unsigned int num_runs = CFArrayGetCount (glyph_runs); |
651 for (unsigned int k = 0; k < chars_len; k++) | 775 DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs); |
| 776 |
| 777 buffer->len = 0; |
| 778 uint32_t status_and = ~0, status_or = 0; |
| 779 |
| 780 const CFRange range_all = CFRangeMake (0, 0); |
| 781 |
| 782 for (unsigned int i = 0; i < num_runs; i++) |
652 { | 783 { |
653 range_record_t *range = last_range; | 784 CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i
)); |
654 while (log_clusters[k] < range->index_first) | 785 CTRunStatus run_status = CTRunGetStatus (run); |
655 » range--; | 786 status_or |= run_status; |
656 while (log_clusters[k] > range->index_last) | 787 status_and &= run_status; |
657 » range++; | 788 DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); |
658 if (range != last_range) | 789 |
659 { | 790 /* CoreText does automatic font fallback (AKA "cascading") for characters |
660 if (last_range->font) | 791 * not supported by the requested font, and provides no way to turn it off
, |
661 » CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - s
tart), | 792 * so we must detect if the returned run uses a font other than the reques
ted |
662 » » » » » kCTFontAttributeName, last_range->font
); | 793 * one and fill in the buffer with .notdef glyphs instead of random glyph |
663 | 794 * indices from a different font. |
664 » start = k; | 795 */ |
665 } | 796 CFDictionaryRef attributes = CTRunGetAttributes (run); |
666 | 797 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attri
butes, kCTFontAttributeName)); |
667 last_range = range; | 798 if (!CFEqual (run_ct_font, font_data->ct_font)) |
| 799 { |
| 800 » /* The run doesn't use our main font instance. We have to figure out |
| 801 » * whether font fallback happened, or this is just CoreText giving us |
| 802 » * another CTFont using the same underlying CGFont. CoreText seems |
| 803 » * to do that in a variety of situations, one of which being vertical |
| 804 » * text, but also perhaps for caching reasons. |
| 805 » * |
| 806 » * First, see if it uses any of our subfonts created to set font feature
s... |
| 807 » * |
| 808 » * Next, compare the CGFont to the one we used to create our fonts. |
| 809 » * Even this doesn't work all the time. |
| 810 » * |
| 811 » * Finally, we compare PS names, which I don't think are unique... |
| 812 » * |
| 813 » * Looks like if we really want to be sure here we have to modify the |
| 814 » * font to change the name table, similar to what we do in the uniscribe |
| 815 » * backend. |
| 816 » * |
| 817 » * However, even that wouldn't work if we were passed in the CGFont to |
| 818 » * begin with. |
| 819 » * |
| 820 » * Webkit uses a slightly different approach: it installs LastResort |
| 821 » * as fallback chain, and then checks PS name of used font against |
| 822 » * LastResort. That one is safe for any font except for LastResort, |
| 823 » * as opposed to ours, which can fail if we are using any uninstalled |
| 824 » * font that has the same name as an installed font. |
| 825 » * |
| 826 » * See: http://github.com/behdad/harfbuzz/pull/36 |
| 827 » */ |
| 828 » bool matched = false; |
| 829 » for (unsigned int i = 0; i < range_records.len; i++) |
| 830 » if (range_records[i].font && CFEqual (run_ct_font, range_records[i].fo
nt)) |
| 831 » { |
| 832 » matched = true; |
| 833 » break; |
| 834 » } |
| 835 » if (!matched) |
| 836 » { |
| 837 » CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); |
| 838 » if (run_cg_font) |
| 839 » { |
| 840 » matched = CFEqual (run_cg_font, face_data); |
| 841 » CFRelease (run_cg_font); |
| 842 » } |
| 843 » } |
| 844 » if (!matched) |
| 845 » { |
| 846 » CFStringRef font_ps_name = CTFontCopyName (font_data->ct_font, kCTFont
PostScriptNameKey); |
| 847 » CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScri
ptNameKey); |
| 848 » CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name
, 0); |
| 849 » CFRelease (run_ps_name); |
| 850 » CFRelease (font_ps_name); |
| 851 » if (result == kCFCompareEqualTo) |
| 852 » matched = true; |
| 853 » } |
| 854 » if (!matched) |
| 855 » { |
| 856 » CFRange range = CTRunGetStringRange (run); |
| 857 DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", |
| 858 » » range.location, range.location + range.length); |
| 859 » if (!buffer->ensure_inplace (buffer->len + range.length)) |
| 860 » goto resize_and_retry; |
| 861 » hb_glyph_info_t *info = buffer->info + buffer->len; |
| 862 |
| 863 » CGGlyph notdef = 0; |
| 864 » double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFo
ntHorizontalOrientation, ¬def, NULL, 1); |
| 865 |
| 866 » unsigned int old_len = buffer->len; |
| 867 » for (CFIndex j = range.location; j < range.location + range.length; j+
+) |
| 868 » { |
| 869 » UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); |
| 870 » if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location
< j) |
| 871 » { |
| 872 » » ch = CFStringGetCharacterAtIndex (string_ref, j - 1); |
| 873 » » if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu)) |
| 874 » » /* This is the second of a surrogate pair. Don't need .notdef |
| 875 » » * for this one. */ |
| 876 » » continue; |
| 877 » } |
| 878 |
| 879 » info->codepoint = notdef; |
| 880 » info->cluster = log_clusters[j]; |
| 881 |
| 882 » info->mask = advance; |
| 883 » info->var1.u32 = 0; |
| 884 » info->var2.u32 = 0; |
| 885 |
| 886 » info++; |
| 887 » buffer->len++; |
| 888 » } |
| 889 » if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) |
| 890 » buffer->reverse_range (old_len, buffer->len); |
| 891 » continue; |
| 892 » } |
| 893 } |
| 894 |
| 895 unsigned int num_glyphs = CTRunGetGlyphCount (run); |
| 896 if (num_glyphs == 0) |
| 897 » continue; |
| 898 |
| 899 if (!buffer->ensure_inplace (buffer->len + num_glyphs)) |
| 900 » goto resize_and_retry; |
| 901 |
| 902 hb_glyph_info_t *run_info = buffer->info + buffer->len; |
| 903 |
| 904 /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always |
| 905 * succeed, and so copying data to our own buffer will be rare. Reports |
| 906 * have it that this changed in OS X 10.10 Yosemite, and NULL is returned |
| 907 * frequently. At any rate, we can test that codepath by setting USE_PTR |
| 908 * to false. */ |
| 909 |
| 910 #define USE_PTR true |
| 911 |
| 912 #define SCRATCH_SAVE() \ |
| 913 unsigned int scratch_size_saved = scratch_size; \ |
| 914 hb_buffer_t::scratch_buffer_t *scratch_saved = scratch |
| 915 |
| 916 #define SCRATCH_RESTORE() \ |
| 917 scratch_size = scratch_size_saved; \ |
| 918 scratch = scratch_saved; |
| 919 |
| 920 { |
| 921 SCRATCH_SAVE(); |
| 922 » const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL; |
| 923 » if (!glyphs) { |
| 924 » ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry)
; |
| 925 » CTRunGetGlyphs (run, range_all, glyph_buf); |
| 926 » glyphs = glyph_buf; |
| 927 » } |
| 928 » const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run)
: NULL; |
| 929 » if (!string_indices) { |
| 930 » ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry)
; |
| 931 » CTRunGetStringIndices (run, range_all, index_buf); |
| 932 » string_indices = index_buf; |
| 933 » } |
| 934 » hb_glyph_info_t *info = run_info; |
| 935 » for (unsigned int j = 0; j < num_glyphs; j++) |
| 936 » { |
| 937 » info->codepoint = glyphs[j]; |
| 938 » info->cluster = log_clusters[string_indices[j]]; |
| 939 » info++; |
| 940 » } |
| 941 » SCRATCH_RESTORE(); |
| 942 } |
| 943 { |
| 944 SCRATCH_SAVE(); |
| 945 » const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL; |
| 946 » if (!positions) { |
| 947 » ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_ret
ry); |
| 948 » CTRunGetPositions (run, range_all, position_buf); |
| 949 » positions = position_buf; |
| 950 » } |
| 951 » double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NU
LL, NULL); |
| 952 » DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); |
| 953 » hb_glyph_info_t *info = run_info; |
| 954 » if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) |
| 955 » { |
| 956 » for (unsigned int j = 0; j < num_glyphs; j++) |
| 957 » { |
| 958 » double advance = (j + 1 < num_glyphs ? positions[j + 1].x : position
s[0].x + run_advance) - positions[j].x; |
| 959 » info->mask = advance; |
| 960 » info->var1.u32 = positions[0].x; /* Yes, zero. */ |
| 961 » info->var2.u32 = positions[j].y; |
| 962 » info++; |
| 963 » } |
| 964 » } |
| 965 » else |
| 966 » { |
| 967 » run_advance = -run_advance; |
| 968 » for (unsigned int j = 0; j < num_glyphs; j++) |
| 969 » { |
| 970 » double advance = (j + 1 < num_glyphs ? positions[j + 1].y : position
s[0].y + run_advance) - positions[j].y; |
| 971 » info->mask = advance; |
| 972 » info->var1.u32 = positions[j].x; |
| 973 » info->var2.u32 = positions[0].y; /* Yes, zero. */ |
| 974 » info++; |
| 975 » } |
| 976 » } |
| 977 » SCRATCH_RESTORE(); |
| 978 } |
| 979 #undef SCRATCH_RESTORE |
| 980 #undef SCRATCH_SAVE |
| 981 #undef USE_PTR |
| 982 #undef ALLOCATE_ARRAY |
| 983 |
| 984 buffer->len += num_glyphs; |
668 } | 985 } |
669 if (start != chars_len && last_range->font) | 986 |
670 CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len
- start - 1), | 987 /* Make sure all runs had the expected direction. */ |
671 » » » » kCTFontAttributeName, last_range->font); | 988 bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); |
672 | 989 assert (bool (status_and & kCTRunStatusRightToLeft) == backward); |
673 for (unsigned int i = 0; i < range_records.len; i++) | 990 assert (bool (status_or & kCTRunStatusRightToLeft) == backward); |
674 if (range_records[i].font) | 991 |
675 » CFRelease (range_records[i].font); | 992 buffer->clear_positions (); |
676 } | 993 |
677 | 994 unsigned int count = buffer->len; |
678 CTLineRef line = CTLineCreateWithAttributedString (attr_string); | 995 hb_glyph_info_t *info = buffer->info; |
679 CFRelease (attr_string); | 996 hb_glyph_position_t *pos = buffer->pos; |
680 | 997 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) |
681 CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); | 998 for (unsigned int i = 0; i < count; i++) |
682 unsigned int num_runs = CFArrayGetCount (glyph_runs); | 999 { |
683 | 1000 » pos->x_advance = info->mask; |
684 buffer->len = 0; | 1001 » pos->x_offset = info->var1.u32; |
685 | 1002 » pos->y_offset = info->var2.u32; |
686 const CFRange range_all = CFRangeMake (0, 0); | 1003 » info++, pos++; |
687 | 1004 } |
688 for (unsigned int i = 0; i < num_runs; i++) | 1005 else |
689 { | 1006 for (unsigned int i = 0; i < count; i++) |
690 CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i); | 1007 { |
691 | 1008 » pos->y_advance = info->mask; |
692 /* CoreText does automatic font fallback (AKA "cascading") for characters | 1009 » pos->x_offset = info->var1.u32; |
693 * not supported by the requested font, and provides no way to turn it off, | 1010 » pos->y_offset = info->var2.u32; |
694 * so we detect if the returned run uses a font other than the requested | 1011 » info++, pos++; |
695 * one and fill in the buffer with .notdef glyphs instead of random glyph | 1012 } |
696 * indices from a different font. | 1013 |
697 */ | 1014 /* Fix up clusters so that we never return out-of-order indices; |
698 CFDictionaryRef attributes = CTRunGetAttributes (run); | 1015 * if core text has reordered glyphs, we'll merge them to the |
699 CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attribu
tes, kCTFontAttributeName)); | 1016 * beginning of the reordered cluster. CoreText is nice enough |
700 CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); | 1017 * to tell us whenever it has produced nonmonotonic results... |
701 if (!CFEqual (run_cg_font, face_data->cg_font)) | 1018 * Note that we assume the input clusters were nonmonotonic to |
| 1019 * begin with. |
| 1020 * |
| 1021 * This does *not* mean we'll form the same clusters as Uniscribe |
| 1022 * or the native OT backend, only that the cluster indices will be |
| 1023 * monotonic in the output buffer. */ |
| 1024 if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) |
702 { | 1025 { |
703 CFRelease (run_cg_font); | 1026 hb_glyph_info_t *info = buffer->info; |
704 | 1027 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) |
705 » CFRange range = CTRunGetStringRange (run); | 1028 { |
706 » buffer->ensure (buffer->len + range.length); | 1029 » unsigned int cluster = info[count - 1].cluster; |
707 » if (buffer->in_error) | 1030 » for (unsigned int i = count - 1; i > 0; i--) |
708 » FAIL ("Buffer resize failed"); | 1031 » { |
709 » hb_glyph_info_t *info = buffer->info + buffer->len; | 1032 » cluster = MIN (cluster, info[i - 1].cluster); |
710 | 1033 » info[i - 1].cluster = cluster; |
711 » CGGlyph notdef = 0; | 1034 » } |
712 » double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFont
HorizontalOrientation, ¬def, NULL, 1); | 1035 } |
713 | 1036 else |
714 for (CFIndex j = range.location; j < range.location + range.length; j++) | 1037 { |
715 » { | 1038 » unsigned int cluster = info[0].cluster; |
716 » UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); | 1039 » for (unsigned int i = 1; i < count; i++) |
717 » if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location <
j) | 1040 » { |
718 » { | 1041 » cluster = MIN (cluster, info[i].cluster); |
719 » ch = CFStringGetCharacterAtIndex (string_ref, j - 1); | 1042 » info[i].cluster = cluster; |
720 » if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu)) | 1043 » } |
721 » /* This is the second of a surrogate pair. Don't need .notdef | 1044 } |
722 » » * for this one. */ | |
723 » continue; | |
724 » } | |
725 | |
726 info->codepoint = notdef; | |
727 » /* TODO We have to fixup clusters later. See vis_clusters in | |
728 » * hb-uniscribe.cc for example. */ | |
729 info->cluster = j; | |
730 | |
731 info->mask = advance; | |
732 info->var1.u32 = 0; | |
733 info->var2.u32 = 0; | |
734 | |
735 » info++; | |
736 » buffer->len++; | |
737 } | |
738 continue; | |
739 } | |
740 CFRelease (run_cg_font); | |
741 | |
742 unsigned int num_glyphs = CTRunGetGlyphCount (run); | |
743 if (num_glyphs == 0) | |
744 continue; | |
745 | |
746 buffer->ensure (buffer->len + num_glyphs); | |
747 | |
748 scratch = buffer->get_scratch_buffer (&scratch_size); | |
749 | |
750 /* Testing indicates that CTRunGetGlyphsPtr, etc (almost?) always | |
751 * succeed, and so copying data to our own buffer will be rare. */ | |
752 | |
753 const CGGlyph* glyphs = CTRunGetGlyphsPtr (run); | |
754 if (!glyphs) { | |
755 ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs); | |
756 CTRunGetGlyphs (run, range_all, glyph_buf); | |
757 glyphs = glyph_buf; | |
758 } | |
759 | |
760 const CGPoint* positions = CTRunGetPositionsPtr (run); | |
761 if (!positions) { | |
762 ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs); | |
763 CTRunGetPositions (run, range_all, position_buf); | |
764 positions = position_buf; | |
765 } | |
766 | |
767 const CFIndex* string_indices = CTRunGetStringIndicesPtr (run); | |
768 if (!string_indices) { | |
769 ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs); | |
770 CTRunGetStringIndices (run, range_all, index_buf); | |
771 string_indices = index_buf; | |
772 } | |
773 | |
774 #undef ALLOCATE_ARRAY | |
775 | |
776 double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NU
LL); | |
777 | |
778 for (unsigned int j = 0; j < num_glyphs; j++) { | |
779 double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x
+ run_width) - positions[j].x; | |
780 | |
781 hb_glyph_info_t *info = &buffer->info[buffer->len]; | |
782 | |
783 info->codepoint = glyphs[j]; | |
784 info->cluster = string_indices[j]; | |
785 | |
786 /* Currently, we do all x-positioning by setting the advance, we never use
x-offset. */ | |
787 info->mask = advance; | |
788 info->var1.u32 = 0; | |
789 info->var2.u32 = positions[j].y; | |
790 | |
791 buffer->len++; | |
792 } | 1045 } |
793 } | 1046 } |
794 | 1047 |
795 buffer->clear_positions (); | 1048 #undef FAIL |
796 | 1049 |
797 unsigned int count = buffer->len; | 1050 fail: |
798 for (unsigned int i = 0; i < count; ++i) { | 1051 if (string_ref) |
799 hb_glyph_info_t *info = &buffer->info[i]; | 1052 CFRelease (string_ref); |
800 hb_glyph_position_t *pos = &buffer->pos[i]; | 1053 if (line) |
801 | 1054 CFRelease (line); |
802 /* TODO vertical */ | 1055 |
803 pos->x_advance = info->mask; | 1056 for (unsigned int i = 0; i < range_records.len; i++) |
804 pos->x_offset = info->var1.u32; | 1057 if (range_records[i].font) |
805 pos->y_offset = info->var2.u32; | 1058 CFRelease (range_records[i].font); |
806 } | 1059 |
807 | 1060 return ret; |
808 /* Fix up clusters so that we never return out-of-order indices; | |
809 * if core text has reordered glyphs, we'll merge them to the | |
810 * beginning of the reordered cluster. | |
811 * | |
812 * This does *not* mean we'll form the same clusters as Uniscribe | |
813 * or the native OT backend, only that the cluster indices will be | |
814 * monotonic in the output buffer. */ | |
815 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { | |
816 unsigned int prev_cluster = 0; | |
817 for (unsigned int i = 0; i < count; i++) { | |
818 unsigned int curr_cluster = buffer->info[i].cluster; | |
819 if (curr_cluster < prev_cluster) { | |
820 for (unsigned int j = i; j > 0; j--) { | |
821 if (buffer->info[j - 1].cluster > curr_cluster) | |
822 buffer->info[j - 1].cluster = curr_cluster; | |
823 else | |
824 break; | |
825 } | |
826 } | |
827 prev_cluster = curr_cluster; | |
828 } | |
829 } else { | |
830 unsigned int prev_cluster = (unsigned int)-1; | |
831 for (unsigned int i = 0; i < count; i++) { | |
832 unsigned int curr_cluster = buffer->info[i].cluster; | |
833 if (curr_cluster > prev_cluster) { | |
834 for (unsigned int j = i; j > 0; j--) { | |
835 if (buffer->info[j - 1].cluster < curr_cluster) | |
836 buffer->info[j - 1].cluster = curr_cluster; | |
837 else | |
838 break; | |
839 } | |
840 } | |
841 prev_cluster = curr_cluster; | |
842 } | |
843 } | |
844 | |
845 CFRelease (string_ref); | |
846 CFRelease (line); | |
847 | |
848 return true; | |
849 } | 1061 } |
850 | 1062 |
851 | 1063 |
852 /* | 1064 /* |
853 * AAT shaper | 1065 * AAT shaper |
854 */ | 1066 */ |
855 | 1067 |
856 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, face) | 1068 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, face) |
857 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, font) | 1069 HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, font) |
858 | 1070 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
934 | 1146 |
935 hb_bool_t | 1147 hb_bool_t |
936 _hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, | 1148 _hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, |
937 hb_font_t *font, | 1149 hb_font_t *font, |
938 hb_buffer_t *buffer, | 1150 hb_buffer_t *buffer, |
939 const hb_feature_t *features, | 1151 const hb_feature_t *features, |
940 unsigned int num_features) | 1152 unsigned int num_features) |
941 { | 1153 { |
942 return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); | 1154 return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); |
943 } | 1155 } |
OLD | NEW |