OLD | NEW |
1 /* | 1 /* |
2 * Copyright © 2010 Google, Inc. | 2 * Copyright © 2010 Google, Inc. |
3 * | 3 * |
4 * This is part of HarfBuzz, a text shaping library. | 4 * This is part of HarfBuzz, a text shaping library. |
5 * | 5 * |
6 * Permission is hereby granted, without written agreement and without | 6 * Permission is hereby granted, without written agreement and without |
7 * license or royalty fees, to use, copy, modify, and distribute this | 7 * license or royalty fees, to use, copy, modify, and distribute this |
8 * software and its documentation for any purpose, provided that the | 8 * software and its documentation for any purpose, provided that the |
9 * above copyright notice and the following two paragraphs appear in | 9 * above copyright notice and the following two paragraphs appear in |
10 * all copies of this software. | 10 * all copies of this software. |
11 * | 11 * |
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 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 | 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 * DAMAGE. | 16 * DAMAGE. |
17 * | 17 * |
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | 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 | 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 * | 23 * |
24 * Google Author(s): Behdad Esfahbod | 24 * Google Author(s): Behdad Esfahbod |
25 */ | 25 */ |
26 | 26 |
27 #include "hb-ot-shape-complex-private.hh" | 27 #include "hb-ot-shape-complex-private.hh" |
28 | 28 |
29 | 29 |
30 /* TODO Add kana, hangul, and other small sahpers here */ | 30 /* TODO Add kana, and other small shapers here */ |
31 | 31 |
32 /* When adding trivial shapers, eg. kana, hangul, etc, we can either | 32 /* When adding trivial shapers, eg. kana, hangul, etc, we can either |
33 * add a full shaper enum value for them, or switch on the script in | 33 * add a full shaper enum value for them, or switch on the script in |
34 * the default complex shaper. The former is faster, so I think that's | 34 * the default complex shaper. The former is faster, so I think that's |
35 * what we would do, and hence the default complex shaper shall remain | 35 * what we would do, and hence the default complex shaper shall remain |
36 * empty. | 36 * empty. |
37 */ | 37 */ |
38 | 38 |
39 void | 39 void |
40 _hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map, const h
b_segment_properties_t *props) | 40 _hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSE
D, |
| 41 » » » » » const hb_segment_properties_t *pr
ops HB_UNUSED) |
41 { | 42 { |
42 } | 43 } |
43 | 44 |
44 bool | 45 hb_ot_shape_normalization_mode_t |
45 _hb_ot_shape_complex_prefer_decomposed_default (void) | 46 _hb_ot_shape_complex_normalization_preference_default (void) |
46 { | 47 { |
47 return FALSE; | 48 return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; |
48 } | 49 } |
49 | 50 |
50 void | 51 void |
51 _hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map, hb_buffer_t *buffer) | 52 _hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map HB_UNUSED, |
| 53 » » » » » hb_buffer_t *buffer HB_UNUSED, |
| 54 » » » » » hb_font_t *font HB_UNUSED) |
52 { | 55 { |
53 } | 56 } |
54 | 57 |
55 | 58 |
| 59 |
| 60 /* Hangul shaper */ |
| 61 |
| 62 static const hb_tag_t hangul_features[] = |
| 63 { |
| 64 HB_TAG('l','j','m','o'), |
| 65 HB_TAG('v','j','m','o'), |
| 66 HB_TAG('t','j','m','o'), |
| 67 }; |
| 68 |
| 69 void |
| 70 _hb_ot_shape_complex_collect_features_hangul (hb_ot_map_builder_t *map, |
| 71 const hb_segment_properties_t *pro
ps HB_UNUSED) |
| 72 { |
| 73 for (unsigned int i = 0; i < ARRAY_LENGTH (hangul_features); i++) |
| 74 map->add_bool_feature (hangul_features[i]); |
| 75 } |
| 76 |
| 77 hb_ot_shape_normalization_mode_t |
| 78 _hb_ot_shape_complex_normalization_preference_hangul (void) |
| 79 { |
| 80 return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL; |
| 81 } |
| 82 |
| 83 void |
| 84 _hb_ot_shape_complex_setup_masks_hangul (hb_ot_map_t *map HB_UNUSED, |
| 85 hb_buffer_t *buffer HB_UNUSED, |
| 86 hb_font_t *font HB_UNUSED) |
| 87 { |
| 88 } |
| 89 |
| 90 |
| 91 |
| 92 /* Thai / Lao shaper */ |
| 93 |
| 94 void |
| 95 _hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map HB_UNUSED, |
| 96 const hb_segment_properties_t *props
HB_UNUSED) |
| 97 { |
| 98 } |
| 99 |
| 100 hb_ot_shape_normalization_mode_t |
| 101 _hb_ot_shape_complex_normalization_preference_thai (void) |
| 102 { |
| 103 return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL; |
| 104 } |
| 105 |
| 106 void |
| 107 _hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED, |
| 108 hb_buffer_t *buffer, |
| 109 hb_font_t *font HB_UNUSED) |
| 110 { |
| 111 /* The following is NOT specified in the MS OT Thai spec, however, it seems |
| 112 * to be what Uniscribe and other engines implement. According to Eric Muller
: |
| 113 * |
| 114 * When you have a sara am, decompose it in nikhahit + sara a, *and* mode the |
| 115 * nihka hit backwards over any *tone* mark (0E48-0E4B). |
| 116 * |
| 117 * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32> |
| 118 * |
| 119 * This reordering is legit only when the nikhahit comes from a sara am, not |
| 120 * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably |
| 121 * not what a u↪ser wanted, but the rendering is nevertheless nikhahit above |
| 122 * chattawa. |
| 123 * |
| 124 * Same for Lao. |
| 125 */ |
| 126 |
| 127 /* |
| 128 * Here are the characters of significance: |
| 129 * |
| 130 * Thai Lao |
| 131 * SARA AM: U+0E33 U+0EB3 |
| 132 * SARA AA: U+0E32 U+0EB2 |
| 133 * Nikhahit: U+0E4D U+0ECD |
| 134 * |
| 135 * Tone marks: |
| 136 * Thai: <0E48..0E4B> CCC=107 |
| 137 * Lao: <0EC8..0ECB> CCC=122 |
| 138 * |
| 139 * Note how the Lao versions are the same as Thai + 0x80. |
| 140 */ |
| 141 |
| 142 /* We only get one script at a time, so a script-agnostic implementation |
| 143 * is adequate here. */ |
| 144 #define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33) |
| 145 #define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D) |
| 146 #define SARA_AA_FROM_SARA_AM(x) ((x) - 1) |
| 147 #define IS_TONE_MARK(x) (((x) & ~0x0083) == 0x0E48) |
| 148 |
| 149 buffer->clear_output (); |
| 150 unsigned int count = buffer->len; |
| 151 for (buffer->idx = 0; buffer->idx < count;) |
| 152 { |
| 153 hb_codepoint_t u = buffer->cur().codepoint; |
| 154 if (likely (!IS_SARA_AM (u))) { |
| 155 buffer->next_glyph (); |
| 156 continue; |
| 157 } |
| 158 |
| 159 /* Is SARA AM. Decompose and reorder. */ |
| 160 hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)), |
| 161 hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))}; |
| 162 buffer->replace_glyphs (1, 2, decomposed); |
| 163 if (unlikely (buffer->in_error)) |
| 164 return; |
| 165 |
| 166 /* Ok, let's see... */ |
| 167 unsigned int end = buffer->out_len; |
| 168 unsigned int start = end - 2; |
| 169 while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint)) |
| 170 start--; |
| 171 |
| 172 /* Move Nikhahit (end-2) to the beginning */ |
| 173 hb_glyph_info_t t = buffer->out_info[end - 2]; |
| 174 memmove (buffer->out_info + start + 1, |
| 175 buffer->out_info + start, |
| 176 sizeof (buffer->out_info[0]) * (end - start - 2)); |
| 177 buffer->out_info[start] = t; |
| 178 |
| 179 /* XXX Make this easier! */ |
| 180 /* Make cluster */ |
| 181 for (; start > 0 && buffer->out_info[start - 1].cluster == buffer->out_info[
start].cluster; start--) |
| 182 ; |
| 183 for (; buffer->idx < count;) |
| 184 if (buffer->cur().cluster == buffer->prev().cluster) |
| 185 buffer->next_glyph (); |
| 186 else |
| 187 break; |
| 188 end = buffer->out_len; |
| 189 |
| 190 buffer->merge_out_clusters (start, end); |
| 191 } |
| 192 buffer->swap_buffers (); |
| 193 } |
OLD | NEW |