1 /* 2 * Implementation of Uniscribe Script Processor (usp10.dll) 3 * 4 * Copyright 2005 Steven Edwards for CodeWeavers 5 * Copyright 2006 Hans Leidekker 6 * Copyright 2010 CodeWeavers, Aric Stewart 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 * 22 * Notes: 23 * Uniscribe allows for processing of complex scripts such as joining 24 * and filtering characters and bi-directional text with custom line breaks. 25 */ 26 27 #include <stdarg.h> 28 #include <stdlib.h> 29 #include <math.h> 30 31 #include "windef.h" 32 #include "winbase.h" 33 #include "wingdi.h" 34 #include "winuser.h" 35 #include "winnls.h" 36 #include "winreg.h" 37 #include "usp10.h" 38 39 #include "usp10_internal.h" 40 41 #include "wine/debug.h" 42 #include "wine/heap.h" 43 #include "wine/unicode.h" 44 45 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe); 46 47 static const struct usp10_script_range 48 { 49 enum usp10_script script; 50 DWORD rangeFirst; 51 DWORD rangeLast; 52 enum usp10_script numericScript; 53 enum usp10_script punctScript; 54 } 55 script_ranges[] = 56 { 57 /* Basic Latin: U+0000–U+007A */ 58 { Script_Latin, 0x00, 0x07a , Script_Numeric, Script_Punctuation}, 59 /* Latin-1 Supplement: U+0080–U+00FF */ 60 /* Latin Extended-A: U+0100–U+017F */ 61 /* Latin Extended-B: U+0180–U+024F */ 62 /* IPA Extensions: U+0250–U+02AF */ 63 /* Spacing Modifier Letters:U+02B0–U+02FF */ 64 { Script_Latin, 0x80, 0x2ff , Script_Numeric2, Script_Punctuation}, 65 /* Combining Diacritical Marks : U+0300–U+036F */ 66 { Script_Diacritical,0x300, 0x36f, 0, 0}, 67 /* Greek: U+0370–U+03FF */ 68 { Script_Greek, 0x370, 0x3ff, 0, 0}, 69 /* Cyrillic: U+0400–U+04FF */ 70 /* Cyrillic Supplement: U+0500–U+052F */ 71 { Script_Cyrillic, 0x400, 0x52f, 0, 0}, 72 /* Armenian: U+0530–U+058F */ 73 { Script_Armenian, 0x530, 0x58f, 0, 0}, 74 /* Hebrew: U+0590–U+05FF */ 75 { Script_Hebrew, 0x590, 0x5ff, 0, 0}, 76 /* Arabic: U+0600–U+06FF */ 77 { Script_Arabic, 0x600, 0x6ef, Script_Arabic_Numeric, 0}, 78 /* Defined by Windows */ 79 { Script_Persian, 0x6f0, 0x6f9, 0, 0}, 80 /* Continue Arabic: U+0600–U+06FF */ 81 { Script_Arabic, 0x6fa, 0x6ff, 0, 0}, 82 /* Syriac: U+0700–U+074F*/ 83 { Script_Syriac, 0x700, 0x74f, 0, 0}, 84 /* Arabic Supplement: U+0750–U+077F */ 85 { Script_Arabic, 0x750, 0x77f, 0, 0}, 86 /* Thaana: U+0780–U+07BF */ 87 { Script_Thaana, 0x780, 0x7bf, 0, 0}, 88 /* N’Ko: U+07C0–U+07FF */ 89 { Script_NKo, 0x7c0, 0x7ff, 0, 0}, 90 /* Devanagari: U+0900–U+097F */ 91 { Script_Devanagari, 0x900, 0x97f, Script_Devanagari_Numeric, 0}, 92 /* Bengali: U+0980–U+09FF */ 93 { Script_Bengali, 0x980, 0x9ff, Script_Bengali_Numeric, 0}, 94 /* Gurmukhi: U+0A00–U+0A7F*/ 95 { Script_Gurmukhi, 0xa00, 0xa7f, Script_Gurmukhi_Numeric, 0}, 96 /* Gujarati: U+0A80–U+0AFF*/ 97 { Script_Gujarati, 0xa80, 0xaff, Script_Gujarati_Numeric, 0}, 98 /* Oriya: U+0B00–U+0B7F */ 99 { Script_Oriya, 0xb00, 0xb7f, Script_Oriya_Numeric, 0}, 100 /* Tamil: U+0B80–U+0BFF */ 101 { Script_Tamil, 0xb80, 0xbff, Script_Tamil_Numeric, 0}, 102 /* Telugu: U+0C00–U+0C7F */ 103 { Script_Telugu, 0xc00, 0xc7f, Script_Telugu_Numeric, 0}, 104 /* Kannada: U+0C80–U+0CFF */ 105 { Script_Kannada, 0xc80, 0xcff, Script_Kannada_Numeric, 0}, 106 /* Malayalam: U+0D00–U+0D7F */ 107 { Script_Malayalam, 0xd00, 0xd7f, Script_Malayalam_Numeric, 0}, 108 /* Sinhala: U+0D80–U+0DFF */ 109 { Script_Sinhala, 0xd80, 0xdff, 0, 0}, 110 /* Thai: U+0E00–U+0E7F */ 111 { Script_Thai, 0xe00, 0xe7f, Script_Thai_Numeric, 0}, 112 /* Lao: U+0E80–U+0EFF */ 113 { Script_Lao, 0xe80, 0xeff, Script_Lao_Numeric, 0}, 114 /* Tibetan: U+0F00–U+0FFF */ 115 { Script_Tibetan, 0xf00, 0xfff, 0, 0}, 116 /* Myanmar: U+1000–U+109F */ 117 { Script_Myanmar, 0x1000, 0x109f, Script_Myanmar_Numeric, 0}, 118 /* Georgian: U+10A0–U+10FF */ 119 { Script_Georgian, 0x10a0, 0x10ff, 0, 0}, 120 /* Hangul Jamo: U+1100–U+11FF */ 121 { Script_Hangul, 0x1100, 0x11ff, 0, 0}, 122 /* Ethiopic: U+1200–U+137F */ 123 /* Ethiopic Extensions: U+1380–U+139F */ 124 { Script_Ethiopic, 0x1200, 0x139f, 0, 0}, 125 /* Cherokee: U+13A0–U+13FF */ 126 { Script_Cherokee, 0x13a0, 0x13ff, 0, 0}, 127 /* Canadian Aboriginal Syllabics: U+1400–U+167F */ 128 { Script_Canadian, 0x1400, 0x167f, 0, 0}, 129 /* Ogham: U+1680–U+169F */ 130 { Script_Ogham, 0x1680, 0x169f, 0, 0}, 131 /* Runic: U+16A0–U+16F0 */ 132 { Script_Runic, 0x16a0, 0x16f0, 0, 0}, 133 /* Khmer: U+1780–U+17FF */ 134 { Script_Khmer, 0x1780, 0x17ff, Script_Khmer_Numeric, 0}, 135 /* Mongolian: U+1800–U+18AF */ 136 { Script_Mongolian, 0x1800, 0x18af, Script_Mongolian_Numeric, 0}, 137 /* Canadian Aboriginal Syllabics Extended: U+18B0–U+18FF */ 138 { Script_Canadian, 0x18b0, 0x18ff, 0, 0}, 139 /* Tai Le: U+1950–U+197F */ 140 { Script_Tai_Le, 0x1950, 0x197f, 0, 0}, 141 /* New Tai Lue: U+1980–U+19DF */ 142 { Script_New_Tai_Lue,0x1980, 0x19df, Script_New_Tai_Lue_Numeric, 0}, 143 /* Khmer Symbols: U+19E0–U+19FF */ 144 { Script_Khmer, 0x19e0, 0x19ff, Script_Khmer_Numeric, 0}, 145 /* Vedic Extensions: U+1CD0-U+1CFF */ 146 { Script_Devanagari, 0x1cd0, 0x1cff, Script_Devanagari_Numeric, 0}, 147 /* Phonetic Extensions: U+1D00–U+1DBF */ 148 { Script_Latin, 0x1d00, 0x1dbf, 0, 0}, 149 /* Combining Diacritical Marks Supplement: U+1DC0–U+1DFF */ 150 { Script_Diacritical,0x1dc0, 0x1dff, 0, 0}, 151 /* Latin Extended Additional: U+1E00–U+1EFF */ 152 { Script_Latin, 0x1e00, 0x1eff, 0, 0}, 153 /* Greek Extended: U+1F00–U+1FFF */ 154 { Script_Greek, 0x1f00, 0x1fff, 0, 0}, 155 /* General Punctuation: U+2000 –U+206f */ 156 { Script_Latin, 0x2000, 0x206f, 0, 0}, 157 /* Superscripts and Subscripts : U+2070 –U+209f */ 158 /* Currency Symbols : U+20a0 –U+20cf */ 159 { Script_Numeric2, 0x2070, 0x2070, 0, 0}, 160 { Script_Latin, 0x2071, 0x2073, 0, 0}, 161 { Script_Numeric2, 0x2074, 0x2079, 0, 0}, 162 { Script_Latin, 0x207a, 0x207f, 0, 0}, 163 { Script_Numeric2, 0x2080, 0x2089, 0, 0}, 164 { Script_Latin, 0x208a, 0x20cf, 0, 0}, 165 /* Letterlike Symbols : U+2100 –U+214f */ 166 /* Number Forms : U+2150 –U+218f */ 167 /* Arrows : U+2190 –U+21ff */ 168 /* Mathematical Operators : U+2200 –U+22ff */ 169 /* Miscellaneous Technical : U+2300 –U+23ff */ 170 /* Control Pictures : U+2400 –U+243f */ 171 /* Optical Character Recognition : U+2440 –U+245f */ 172 /* Enclosed Alphanumerics : U+2460 –U+24ff */ 173 /* Box Drawing : U+2500 –U+25ff */ 174 /* Block Elements : U+2580 –U+259f */ 175 /* Geometric Shapes : U+25a0 –U+25ff */ 176 /* Miscellaneous Symbols : U+2600 –U+26ff */ 177 /* Dingbats : U+2700 –U+27bf */ 178 /* Miscellaneous Mathematical Symbols-A : U+27c0 –U+27ef */ 179 /* Supplemental Arrows-A : U+27f0 –U+27ff */ 180 { Script_Latin, 0x2100, 0x27ff, 0, 0}, 181 /* Braille Patterns: U+2800–U+28FF */ 182 { Script_Braille, 0x2800, 0x28ff, 0, 0}, 183 /* Supplemental Arrows-B : U+2900 –U+297f */ 184 /* Miscellaneous Mathematical Symbols-B : U+2980 –U+29ff */ 185 /* Supplemental Mathematical Operators : U+2a00 –U+2aff */ 186 /* Miscellaneous Symbols and Arrows : U+2b00 –U+2bff */ 187 { Script_Latin, 0x2900, 0x2bff, 0, 0}, 188 /* Latin Extended-C: U+2C60–U+2C7F */ 189 { Script_Latin, 0x2c60, 0x2c7f, 0, 0}, 190 /* Georgian: U+2D00–U+2D2F */ 191 { Script_Georgian, 0x2d00, 0x2d2f, 0, 0}, 192 /* Tifinagh: U+2D30–U+2D7F */ 193 { Script_Tifinagh, 0x2d30, 0x2d7f, 0, 0}, 194 /* Ethiopic Extensions: U+2D80–U+2DDF */ 195 { Script_Ethiopic, 0x2d80, 0x2ddf, 0, 0}, 196 /* Cyrillic Extended-A: U+2DE0–U+2DFF */ 197 { Script_Cyrillic, 0x2de0, 0x2dff, 0, 0}, 198 /* CJK Radicals Supplement: U+2E80–U+2EFF */ 199 /* Kangxi Radicals: U+2F00–U+2FDF */ 200 { Script_CJK_Han, 0x2e80, 0x2fdf, 0, 0}, 201 /* Ideographic Description Characters: U+2FF0–U+2FFF */ 202 { Script_Ideograph ,0x2ff0, 0x2fff, 0, 0}, 203 /* CJK Symbols and Punctuation: U+3000–U+303F */ 204 { Script_Ideograph ,0x3000, 0x3004, 0, 0}, 205 { Script_CJK_Han ,0x3005, 0x3005, 0, 0}, 206 { Script_Ideograph ,0x3006, 0x3006, 0, 0}, 207 { Script_CJK_Han ,0x3007, 0x3007, 0, 0}, 208 { Script_Ideograph ,0x3008, 0x3020, 0, 0}, 209 { Script_CJK_Han ,0x3021, 0x3029, 0, 0}, 210 { Script_Ideograph ,0x302a, 0x3030, 0, 0}, 211 /* Kana Marks: */ 212 { Script_Kana ,0x3031, 0x3035, 0, 0}, 213 { Script_Ideograph ,0x3036, 0x3037, 0, 0}, 214 { Script_CJK_Han ,0x3038, 0x303b, 0, 0}, 215 { Script_Ideograph ,0x303c, 0x303f, 0, 0}, 216 /* Hiragana: U+3040–U+309F */ 217 /* Katakana: U+30A0–U+30FF */ 218 { Script_Kana ,0x3040, 0x30ff, 0, 0}, 219 /* Bopomofo: U+3100–U+312F */ 220 { Script_Bopomofo ,0x3100, 0x312f, 0, 0}, 221 /* Hangul Compatibility Jamo: U+3130–U+318F */ 222 { Script_Hangul ,0x3130, 0x318f, 0, 0}, 223 /* Kanbun: U+3190–U+319F */ 224 { Script_Ideograph ,0x3190, 0x319f, 0, 0}, 225 /* Bopomofo Extended: U+31A0–U+31BF */ 226 { Script_Bopomofo ,0x31a0, 0x31bf, 0, 0}, 227 /* CJK Strokes: U+31C0–U+31EF */ 228 { Script_Ideograph ,0x31c0, 0x31ef, 0, 0}, 229 /* Katakana Phonetic Extensions: U+31F0–U+31FF */ 230 { Script_Kana ,0x31f0, 0x31ff, 0, 0}, 231 /* Enclosed CJK Letters and Months: U+3200–U+32FF */ 232 { Script_Hangul ,0x3200, 0x321f, 0, 0}, 233 { Script_Ideograph ,0x3220, 0x325f, 0, 0}, 234 { Script_Hangul ,0x3260, 0x327f, 0, 0}, 235 { Script_Ideograph ,0x3280, 0x32ef, 0, 0}, 236 { Script_Kana ,0x32d0, 0x31ff, 0, 0}, 237 /* CJK Compatibility: U+3300–U+33FF*/ 238 { Script_Kana ,0x3300, 0x3357, 0, 0}, 239 { Script_Ideograph ,0x3358, 0x33ff, 0, 0}, 240 /* CJK Unified Ideographs Extension A: U+3400–U+4DBF */ 241 { Script_CJK_Han ,0x3400, 0x4dbf, 0, 0}, 242 /* CJK Unified Ideographs: U+4E00–U+9FFF */ 243 { Script_CJK_Han ,0x4e00, 0x9fff, 0, 0}, 244 /* Yi: U+A000–U+A4CF */ 245 { Script_Yi ,0xa000, 0xa4cf, 0, 0}, 246 /* Vai: U+A500–U+A63F */ 247 { Script_Vai ,0xa500, 0xa63f, Script_Vai_Numeric, 0}, 248 /* Cyrillic Extended-B: U+A640–U+A69F */ 249 { Script_Cyrillic, 0xa640, 0xa69f, 0, 0}, 250 /* Modifier Tone Letters: U+A700–U+A71F */ 251 /* Latin Extended-D: U+A720–U+A7FF */ 252 { Script_Latin, 0xa700, 0xa7ff, 0, 0}, 253 /* Phags-pa: U+A840–U+A87F */ 254 { Script_Phags_pa, 0xa840, 0xa87f, 0, 0}, 255 /* Devanagari Extended: U+A8E0-U+A8FF */ 256 { Script_Devanagari, 0xa8e0, 0xa8ff, Script_Devanagari_Numeric, 0}, 257 /* Myanmar Extended-A: U+AA60–U+AA7F */ 258 { Script_Myanmar, 0xaa60, 0xaa7f, Script_Myanmar_Numeric, 0}, 259 /* Hangul Jamo Extended-A: U+A960–U+A97F */ 260 { Script_Hangul, 0xa960, 0xa97f, 0, 0}, 261 /* Hangul Syllables: U+AC00–U+D7A3 */ 262 { Script_Hangul, 0xac00, 0xd7a3, 0, 0}, 263 /* Hangul Jamo Extended-B: U+D7B0–U+D7FF */ 264 { Script_Hangul, 0xd7b0, 0xd7ff, 0, 0}, 265 /* Surrogates Area: U+D800–U+DFFF */ 266 { Script_Surrogates, 0xd800, 0xdbfe, 0, 0}, 267 { Script_Private, 0xdbff, 0xdc00, 0, 0}, 268 { Script_Surrogates, 0xdc01, 0xdfff, 0, 0}, 269 /* Private Use Area: U+E000–U+F8FF */ 270 { Script_Private, 0xe000, 0xf8ff, 0, 0}, 271 /* CJK Compatibility Ideographs: U+F900–U+FAFF */ 272 { Script_CJK_Han ,0xf900, 0xfaff, 0, 0}, 273 /* Latin Ligatures: U+FB00–U+FB06 */ 274 { Script_Latin, 0xfb00, 0xfb06, 0, 0}, 275 /* Armenian ligatures U+FB13..U+FB17 */ 276 { Script_Armenian, 0xfb13, 0xfb17, 0, 0}, 277 /* Alphabetic Presentation Forms: U+FB1D–U+FB4F */ 278 { Script_Hebrew, 0xfb1d, 0xfb4f, 0, 0}, 279 /* Arabic Presentation Forms-A: U+FB50–U+FDFF*/ 280 { Script_Arabic, 0xfb50, 0xfdff, 0, 0}, 281 /* Vertical Forms: U+FE10–U+FE1F */ 282 /* Combining Half Marks: U+FE20–U+FE2F */ 283 /* CJK Compatibility Forms: U+FE30–U+FE4F */ 284 /* Small Form Variants: U+FE50–U+FE6F */ 285 { Script_Ideograph ,0xfe10, 0xfe6f, 0, 0}, 286 /* Arabic Presentation Forms-B: U+FE70–U+FEFF*/ 287 { Script_Arabic, 0xfe70, 0xfeff, 0, 0}, 288 /* Halfwidth and Fullwidth Forms: U+FF00–FFEF */ 289 { Script_Ideograph ,0xff00, 0xff64, Script_Numeric2, 0}, 290 { Script_Kana ,0xff65, 0xff9f, 0, 0}, 291 { Script_Hangul ,0xffa0, 0xffdf, 0, 0}, 292 { Script_Ideograph ,0xffe0, 0xffef, 0, 0}, 293 /* Plane - 1 */ 294 /* Deseret: U+10400–U+1044F */ 295 { Script_Deseret, 0x10400, 0x1044F, 0, 0}, 296 /* Osmanya: U+10480–U+104AF */ 297 { Script_Osmanya, 0x10480, 0x104AF, Script_Osmanya_Numeric, 0}, 298 /* Mathematical Alphanumeric Symbols: U+1D400–U+1D7FF */ 299 { Script_MathAlpha, 0x1D400, 0x1D7FF, 0, 0}, 300 }; 301 302 /* this must be in order so that the index matches the Script value */ 303 const scriptData scriptInformation[] = { 304 {{SCRIPT_UNDEFINED, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 305 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 306 0x00000000, 307 {0}}, 308 {{Script_Latin, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 309 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 310 MS_MAKE_TAG('l','a','t','n'), 311 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 312 {{Script_CR, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 313 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 314 0x00000000, 315 {0}}, 316 {{Script_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 317 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 318 0x00000000, 319 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 320 {{Script_Control, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 321 {LANG_ENGLISH, 0, 1, 0, 0, ANSI_CHARSET, 1, 0, 0, 0, 0, 0, 1, 0, 0}, 322 0x00000000, 323 {0}}, 324 {{Script_Punctuation, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 325 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 326 0x00000000, 327 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 328 {{Script_Arabic, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}}, 329 {LANG_ARABIC, 0, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 330 MS_MAKE_TAG('a','r','a','b'), 331 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 332 {{Script_Arabic_Numeric, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}}, 333 {LANG_ARABIC, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 334 MS_MAKE_TAG('a','r','a','b'), 335 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 336 {{Script_Hebrew, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}}, 337 {LANG_HEBREW, 0, 1, 0, 1, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 338 MS_MAKE_TAG('h','e','b','r'), 339 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 340 {{Script_Syriac, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}}, 341 {LANG_SYRIAC, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 1, 0}, 342 MS_MAKE_TAG('s','y','r','c'), 343 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}}, 344 {{Script_Persian, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}}, 345 {LANG_PERSIAN, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 346 MS_MAKE_TAG('a','r','a','b'), 347 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 348 {{Script_Thaana, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}}, 349 {LANG_DIVEHI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 350 MS_MAKE_TAG('t','h','a','a'), 351 {'M','V',' ','B','o','l','i',0}}, 352 {{Script_Greek, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 353 {LANG_GREEK, 0, 0, 0, 0, GREEK_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 354 MS_MAKE_TAG('g','r','e','k'), 355 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 356 {{Script_Cyrillic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 357 {LANG_RUSSIAN, 0, 0, 0, 0, RUSSIAN_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 358 MS_MAKE_TAG('c','y','r','l'), 359 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 360 {{Script_Armenian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 361 {LANG_ARMENIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 362 MS_MAKE_TAG('a','r','m','n'), 363 {'S','y','l','f','a','e','n',0}}, 364 {{Script_Georgian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 365 {LANG_GEORGIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 366 MS_MAKE_TAG('g','e','o','r'), 367 {'S','y','l','f','a','e','n',0}}, 368 {{Script_Sinhala, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 369 {LANG_SINHALESE, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 370 MS_MAKE_TAG('s','i','n','h'), 371 {'I','s','k','o','o','l','a',' ','P','o','t','a',0}}, 372 {{Script_Tibetan, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 373 {LANG_TIBETAN, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0}, 374 MS_MAKE_TAG('t','i','b','t'), 375 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}}, 376 {{Script_Tibetan_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 377 {LANG_TIBETAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 378 MS_MAKE_TAG('t','i','b','t'), 379 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}}, 380 {{Script_Phags_pa, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 381 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 382 MS_MAKE_TAG('p','h','a','g'), 383 {'M','i','c','r','o','s','o','f','t',' ','P','h','a','g','s','P','a',0}}, 384 {{Script_Thai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 385 {LANG_THAI, 0, 1, 1, 1, THAI_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 1}, 386 MS_MAKE_TAG('t','h','a','i'), 387 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 388 {{Script_Thai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 389 {LANG_THAI, 1, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 390 MS_MAKE_TAG('t','h','a','i'), 391 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 392 {{Script_Lao, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 393 {LANG_LAO, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0}, 394 MS_MAKE_TAG('l','a','o',' '), 395 {'D','o','k','C','h','a','m','p','a',0}}, 396 {{Script_Lao_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 397 {LANG_LAO, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 398 MS_MAKE_TAG('l','a','o',' '), 399 {'D','o','k','C','h','a','m','p','a',0}}, 400 {{Script_Devanagari, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 401 {LANG_HINDI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 402 MS_MAKE_TAG('d','e','v','a'), 403 {'M','a','n','g','a','l',0}}, 404 {{Script_Devanagari_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 405 {LANG_HINDI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 406 MS_MAKE_TAG('d','e','v','a'), 407 {'M','a','n','g','a','l',0}}, 408 {{Script_Bengali, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 409 {LANG_BENGALI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 410 MS_MAKE_TAG('b','e','n','g'), 411 {'V','r','i','n','d','a',0}}, 412 {{Script_Bengali_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 413 {LANG_BENGALI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 414 MS_MAKE_TAG('b','e','n','g'), 415 {'V','r','i','n','d','a',0}}, 416 {{Script_Bengali_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 417 {LANG_BENGALI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 418 MS_MAKE_TAG('b','e','n','g'), 419 {'V','r','i','n','d','a',0}}, 420 {{Script_Gurmukhi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 421 {LANG_PUNJABI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 422 MS_MAKE_TAG('g','u','r','u'), 423 {'R','a','a','v','i',0}}, 424 {{Script_Gurmukhi_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 425 {LANG_PUNJABI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 426 MS_MAKE_TAG('g','u','r','u'), 427 {'R','a','a','v','i',0}}, 428 {{Script_Gujarati, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 429 {LANG_GUJARATI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 430 MS_MAKE_TAG('g','u','j','r'), 431 {'S','h','r','u','t','i',0}}, 432 {{Script_Gujarati_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 433 {LANG_GUJARATI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 434 MS_MAKE_TAG('g','u','j','r'), 435 {'S','h','r','u','t','i',0}}, 436 {{Script_Gujarati_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 437 {LANG_GUJARATI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 438 MS_MAKE_TAG('g','u','j','r'), 439 {'S','h','r','u','t','i',0}}, 440 {{Script_Oriya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 441 {LANG_ORIYA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 442 MS_MAKE_TAG('o','r','y','a'), 443 {'K','a','l','i','n','g','a',0}}, 444 {{Script_Oriya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 445 {LANG_ORIYA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 446 MS_MAKE_TAG('o','r','y','a'), 447 {'K','a','l','i','n','g','a',0}}, 448 {{Script_Tamil, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 449 {LANG_TAMIL, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 450 MS_MAKE_TAG('t','a','m','l'), 451 {'L','a','t','h','a',0}}, 452 {{Script_Tamil_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 453 {LANG_TAMIL, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 454 MS_MAKE_TAG('t','a','m','l'), 455 {'L','a','t','h','a',0}}, 456 {{Script_Telugu, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 457 {LANG_TELUGU, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 458 MS_MAKE_TAG('t','e','l','u'), 459 {'G','a','u','t','a','m','i',0}}, 460 {{Script_Telugu_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 461 {LANG_TELUGU, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 462 MS_MAKE_TAG('t','e','l','u'), 463 {'G','a','u','t','a','m','i',0}}, 464 {{Script_Kannada, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 465 {LANG_KANNADA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 466 MS_MAKE_TAG('k','n','d','a'), 467 {'T','u','n','g','a',0}}, 468 {{Script_Kannada_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 469 {LANG_KANNADA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 470 MS_MAKE_TAG('k','n','d','a'), 471 {'T','u','n','g','a',0}}, 472 {{Script_Malayalam, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 473 {LANG_MALAYALAM, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 474 MS_MAKE_TAG('m','l','y','m'), 475 {'K','a','r','t','i','k','a',0}}, 476 {{Script_Malayalam_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 477 {LANG_MALAYALAM, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 478 MS_MAKE_TAG('m','l','y','m'), 479 {'K','a','r','t','i','k','a',0}}, 480 {{Script_Diacritical, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 481 {LANG_ENGLISH, 0, 1, 0, 1, ANSI_CHARSET, 0, 0, 0, 0, 0, 1, 1, 0, 0}, 482 0x00000000, 483 {0}}, 484 {{Script_Punctuation2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 485 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 486 MS_MAKE_TAG('l','a','t','n'), 487 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 488 {{Script_Numeric2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 489 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 490 0x00000000, 491 {0}}, 492 {{Script_Myanmar, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 493 {0x55, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 494 MS_MAKE_TAG('m','y','m','r'), 495 {'M','y','a','n','m','a','r',' ','T','e','x','t',0}}, 496 {{Script_Myanmar_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 497 {0x55, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 498 MS_MAKE_TAG('m','y','m','r'), 499 {0}}, 500 {{Script_Tai_Le, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 501 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 502 MS_MAKE_TAG('t','a','l','e'), 503 {'M','i','c','r','o','s','o','f','t',' ','T','a','i',' ','L','e',0}}, 504 {{Script_New_Tai_Lue, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 505 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 506 MS_MAKE_TAG('t','a','l','u'), 507 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}}, 508 {{Script_New_Tai_Lue_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 509 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 510 MS_MAKE_TAG('t','a','l','u'), 511 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}}, 512 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 513 {0x53, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 514 MS_MAKE_TAG('k','h','m','r'), 515 {'D','a','u','n','P','e','n','h',0}}, 516 {{Script_Khmer_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 517 {0x53, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 518 MS_MAKE_TAG('k','h','m','r'), 519 {'D','a','u','n','P','e','n','h',0}}, 520 {{Script_CJK_Han, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 521 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 522 MS_MAKE_TAG('h','a','n','i'), 523 {0}}, 524 {{Script_Ideograph, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 525 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 526 MS_MAKE_TAG('h','a','n','i'), 527 {0}}, 528 {{Script_Bopomofo, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 529 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 530 MS_MAKE_TAG('b','o','p','o'), 531 {0}}, 532 {{Script_Kana, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 533 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 534 MS_MAKE_TAG('k','a','n','a'), 535 {0}}, 536 {{Script_Hangul, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 537 {LANG_KOREAN, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 538 MS_MAKE_TAG('h','a','n','g'), 539 {0}}, 540 {{Script_Yi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 541 {LANG_ENGLISH, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 542 MS_MAKE_TAG('y','i',' ',' '), 543 {'M','i','c','r','o','s','o','f','t',' ','Y','i',' ','B','a','i','t','i',0}}, 544 {{Script_Ethiopic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 545 {0x5e, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 546 MS_MAKE_TAG('e','t','h','i'), 547 {'N','y','a','l','a',0}}, 548 {{Script_Ethiopic_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 549 {0x5e, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 550 MS_MAKE_TAG('e','t','h','i'), 551 {'N','y','a','l','a',0}}, 552 {{Script_Mongolian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 553 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 554 MS_MAKE_TAG('m','o','n','g'), 555 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}}, 556 {{Script_Mongolian_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 557 {LANG_MONGOLIAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 558 MS_MAKE_TAG('m','o','n','g'), 559 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}}, 560 {{Script_Tifinagh, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 561 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 562 MS_MAKE_TAG('t','f','n','g'), 563 {'E','b','r','i','m','a',0}}, 564 {{Script_NKo, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}}, 565 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 566 MS_MAKE_TAG('n','k','o',' '), 567 {'E','b','r','i','m','a',0}}, 568 {{Script_Vai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 569 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 570 MS_MAKE_TAG('v','a','i',' '), 571 {'E','b','r','i','m','a',0}}, 572 {{Script_Vai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 573 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 574 MS_MAKE_TAG('v','a','i',' '), 575 {'E','b','r','i','m','a',0}}, 576 {{Script_Cherokee, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 577 {0x5c, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 578 MS_MAKE_TAG('c','h','e','r'), 579 {'P','l','a','n','t','a','g','e','n','e','t',' ','C','h','e','r','o','k','e','e',0}}, 580 {{Script_Canadian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 581 {0x5d, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 582 MS_MAKE_TAG('c','a','n','s'), 583 {'E','u','p','h','e','m','i','a',0}}, 584 {{Script_Ogham, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 585 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 586 MS_MAKE_TAG('o','g','a','m'), 587 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}}, 588 {{Script_Runic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 589 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 590 MS_MAKE_TAG('r','u','n','r'), 591 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}}, 592 {{Script_Braille, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 593 {LANG_ENGLISH, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 594 MS_MAKE_TAG('b','r','a','i'), 595 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}}, 596 {{Script_Surrogates, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 597 {LANG_ENGLISH, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0}, 598 0x00000000, 599 {0}}, 600 {{Script_Private, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 601 {0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 1, 0, 0, 0, 0, 1, 0, 0}, 602 0x00000000, 603 {0}}, 604 {{Script_Deseret, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 605 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 606 MS_MAKE_TAG('d','s','r','t'), 607 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}}, 608 {{Script_Osmanya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 609 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 610 MS_MAKE_TAG('o','s','m','a'), 611 {'E','b','r','i','m','a',0}}, 612 {{Script_Osmanya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 613 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 614 MS_MAKE_TAG('o','s','m','a'), 615 {'E','b','r','i','m','a',0}}, 616 {{Script_MathAlpha, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 617 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 618 MS_MAKE_TAG('m','a','t','h'), 619 {'C','a','m','b','r','i','a',' ','M','a','t','h',0}}, 620 {{Script_Hebrew_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 621 {LANG_HEBREW, 0, 1, 0, 0, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 622 MS_MAKE_TAG('h','e','b','r'), 623 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 624 {{Script_Vietnamese_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 625 {LANG_VIETNAMESE, 0, 0, 0, 0, VIETNAMESE_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 626 MS_MAKE_TAG('l','a','t','n'), 627 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 628 {{Script_Thai_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}}, 629 {LANG_THAI, 0, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 630 MS_MAKE_TAG('t','h','a','i'), 631 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}}, 632 }; 633 634 static const SCRIPT_PROPERTIES *script_props[] = 635 { 636 &scriptInformation[0].props, &scriptInformation[1].props, 637 &scriptInformation[2].props, &scriptInformation[3].props, 638 &scriptInformation[4].props, &scriptInformation[5].props, 639 &scriptInformation[6].props, &scriptInformation[7].props, 640 &scriptInformation[8].props, &scriptInformation[9].props, 641 &scriptInformation[10].props, &scriptInformation[11].props, 642 &scriptInformation[12].props, &scriptInformation[13].props, 643 &scriptInformation[14].props, &scriptInformation[15].props, 644 &scriptInformation[16].props, &scriptInformation[17].props, 645 &scriptInformation[18].props, &scriptInformation[19].props, 646 &scriptInformation[20].props, &scriptInformation[21].props, 647 &scriptInformation[22].props, &scriptInformation[23].props, 648 &scriptInformation[24].props, &scriptInformation[25].props, 649 &scriptInformation[26].props, &scriptInformation[27].props, 650 &scriptInformation[28].props, &scriptInformation[29].props, 651 &scriptInformation[30].props, &scriptInformation[31].props, 652 &scriptInformation[32].props, &scriptInformation[33].props, 653 &scriptInformation[34].props, &scriptInformation[35].props, 654 &scriptInformation[36].props, &scriptInformation[37].props, 655 &scriptInformation[38].props, &scriptInformation[39].props, 656 &scriptInformation[40].props, &scriptInformation[41].props, 657 &scriptInformation[42].props, &scriptInformation[43].props, 658 &scriptInformation[44].props, &scriptInformation[45].props, 659 &scriptInformation[46].props, &scriptInformation[47].props, 660 &scriptInformation[48].props, &scriptInformation[49].props, 661 &scriptInformation[50].props, &scriptInformation[51].props, 662 &scriptInformation[52].props, &scriptInformation[53].props, 663 &scriptInformation[54].props, &scriptInformation[55].props, 664 &scriptInformation[56].props, &scriptInformation[57].props, 665 &scriptInformation[58].props, &scriptInformation[59].props, 666 &scriptInformation[60].props, &scriptInformation[61].props, 667 &scriptInformation[62].props, &scriptInformation[63].props, 668 &scriptInformation[64].props, &scriptInformation[65].props, 669 &scriptInformation[66].props, &scriptInformation[67].props, 670 &scriptInformation[68].props, &scriptInformation[69].props, 671 &scriptInformation[70].props, &scriptInformation[71].props, 672 &scriptInformation[72].props, &scriptInformation[73].props, 673 &scriptInformation[74].props, &scriptInformation[75].props, 674 &scriptInformation[76].props, &scriptInformation[77].props, 675 &scriptInformation[78].props, &scriptInformation[79].props, 676 &scriptInformation[80].props, &scriptInformation[81].props 677 }; 678 679 static CRITICAL_SECTION cs_script_cache; 680 static CRITICAL_SECTION_DEBUG cs_script_cache_dbg = 681 { 682 0, 0, &cs_script_cache, 683 { &cs_script_cache_dbg.ProcessLocksList, &cs_script_cache_dbg.ProcessLocksList }, 684 0, 0, { (DWORD_PTR)(__FILE__ ": script_cache") } 685 }; 686 static CRITICAL_SECTION cs_script_cache = { &cs_script_cache_dbg, -1, 0, 0, 0, 0 }; 687 static struct list script_cache_list = LIST_INIT(script_cache_list); 688 689 typedef struct { 690 ScriptCache *sc; 691 int numGlyphs; 692 WORD* glyphs; 693 WORD* pwLogClust; 694 int* piAdvance; 695 SCRIPT_VISATTR* psva; 696 GOFFSET* pGoffset; 697 ABC abc; 698 int iMaxPosX; 699 HFONT fallbackFont; 700 } StringGlyphs; 701 702 enum stringanalysis_flags 703 { 704 SCRIPT_STRING_ANALYSIS_FLAGS_SIZE = 0x1, 705 SCRIPT_STRING_ANALYSIS_FLAGS_INVALID = 0x2, 706 }; 707 708 typedef struct { 709 HDC hdc; 710 DWORD ssa_flags; 711 DWORD flags; 712 int clip_len; 713 int cItems; 714 int cMaxGlyphs; 715 SCRIPT_ITEM* pItem; 716 int numItems; 717 StringGlyphs* glyphs; 718 SCRIPT_LOGATTR* logattrs; 719 SIZE sz; 720 int* logical2visual; 721 } StringAnalysis; 722 723 typedef struct { 724 BOOL ascending; 725 WORD target; 726 } FindGlyph_struct; 727 728 BOOL usp10_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) 729 { 730 SIZE_T max_capacity, new_capacity; 731 void *new_elements; 732 733 if (count <= *capacity) 734 return TRUE; 735 736 max_capacity = ~(SIZE_T)0 / size; 737 if (count > max_capacity) 738 return FALSE; 739 740 new_capacity = max(1, *capacity); 741 while (new_capacity < count && new_capacity <= max_capacity / 2) 742 new_capacity *= 2; 743 if (new_capacity < count) 744 new_capacity = count; 745 746 if (!*elements) 747 new_elements = heap_alloc_zero(new_capacity * size); 748 else 749 new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size); 750 if (!new_elements) 751 return FALSE; 752 753 *elements = new_elements; 754 *capacity = new_capacity; 755 return TRUE; 756 } 757 758 /* TODO Fix font properties on Arabic locale */ 759 static inline BOOL set_cache_font_properties(const HDC hdc, ScriptCache *sc) 760 { 761 sc->sfp.cBytes = sizeof(sc->sfp); 762 763 if (!sc->sfnt) 764 { 765 sc->sfp.wgBlank = sc->tm.tmBreakChar; 766 sc->sfp.wgDefault = sc->tm.tmDefaultChar; 767 sc->sfp.wgInvalid = sc->sfp.wgBlank; 768 sc->sfp.wgKashida = 0xFFFF; 769 sc->sfp.iKashidaWidth = 0; 770 } 771 else 772 { 773 static const WCHAR chars[4] = {0x0020, 0x200B, 0xF71B, 0x0640}; 774 /* U+0020: numeric space 775 U+200B: zero width space 776 U+F71B: unknown char found by black box testing 777 U+0640: kashida */ 778 WORD gi[4]; 779 780 if (GetGlyphIndicesW(hdc, chars, 4, gi, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) 781 { 782 if(gi[0] != 0xFFFF) /* 0xFFFF: index of default non exist char */ 783 sc->sfp.wgBlank = gi[0]; 784 else 785 sc->sfp.wgBlank = 0; 786 787 sc->sfp.wgDefault = 0; 788 789 if (gi[2] != 0xFFFF) 790 sc->sfp.wgInvalid = gi[2]; 791 else if (gi[1] != 0xFFFF) 792 sc->sfp.wgInvalid = gi[1]; 793 else if (gi[0] != 0xFFFF) 794 sc->sfp.wgInvalid = gi[0]; 795 else 796 sc->sfp.wgInvalid = 0; 797 798 sc->sfp.wgKashida = gi[3]; 799 800 sc->sfp.iKashidaWidth = 0; /* TODO */ 801 } 802 else 803 return FALSE; 804 } 805 return TRUE; 806 } 807 808 static inline void get_cache_font_properties(SCRIPT_FONTPROPERTIES *sfp, ScriptCache *sc) 809 { 810 *sfp = sc->sfp; 811 } 812 813 static inline LONG get_cache_height(SCRIPT_CACHE *psc) 814 { 815 return ((ScriptCache *)*psc)->tm.tmHeight; 816 } 817 818 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc) 819 { 820 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily; 821 } 822 823 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, DWORD c) 824 { 825 CacheGlyphPage *page = ((ScriptCache *)*psc)->page[c / 0x10000]; 826 WORD *block; 827 828 if (!page) return 0; 829 block = page->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT]; 830 if (!block) return 0; 831 return block[(c % 0x10000) & GLYPH_BLOCK_MASK]; 832 } 833 834 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph) 835 { 836 CacheGlyphPage **page = &((ScriptCache *)*psc)->page[c / 0x10000]; 837 WORD **block; 838 if (!*page && !(*page = heap_alloc_zero(sizeof(CacheGlyphPage)))) return 0; 839 840 block = &(*page)->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT]; 841 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0; 842 return ((*block)[(c % 0x10000) & GLYPH_BLOCK_MASK] = glyph); 843 } 844 845 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc) 846 { 847 static const ABC nil; 848 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT]; 849 850 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE; 851 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC)); 852 return TRUE; 853 } 854 855 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc) 856 { 857 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT]; 858 859 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE; 860 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC)); 861 return TRUE; 862 } 863 864 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc) 865 { 866 ScriptCache *sc; 867 unsigned size; 868 LOGFONTW lf; 869 870 if (!psc) return E_INVALIDARG; 871 if (*psc) return S_OK; 872 if (!hdc) return E_PENDING; 873 874 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf)) 875 { 876 return E_INVALIDARG; 877 } 878 /* Ensure canonical result by zeroing extra space in lfFaceName */ 879 size = strlenW(lf.lfFaceName); 880 memset(lf.lfFaceName + size, 0, sizeof(lf.lfFaceName) - size * sizeof(WCHAR)); 881 882 EnterCriticalSection(&cs_script_cache); 883 LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry) 884 { 885 if (!memcmp(&sc->lf, &lf, sizeof(lf))) 886 { 887 sc->refcount++; 888 LeaveCriticalSection(&cs_script_cache); 889 *psc = sc; 890 return S_OK; 891 } 892 } 893 LeaveCriticalSection(&cs_script_cache); 894 895 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY; 896 if (!GetTextMetricsW(hdc, &sc->tm)) 897 { 898 heap_free(sc); 899 return E_INVALIDARG; 900 } 901 size = GetOutlineTextMetricsW(hdc, 0, NULL); 902 if (size) 903 { 904 sc->otm = heap_alloc(size); 905 sc->otm->otmSize = size; 906 GetOutlineTextMetricsW(hdc, size, sc->otm); 907 } 908 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR); 909 if (!set_cache_font_properties(hdc, sc)) 910 { 911 heap_free(sc); 912 return E_INVALIDARG; 913 } 914 sc->lf = lf; 915 sc->refcount = 1; 916 *psc = sc; 917 918 EnterCriticalSection(&cs_script_cache); 919 list_add_head(&script_cache_list, &sc->entry); 920 LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry) 921 { 922 if (sc != *psc && !memcmp(&sc->lf, &lf, sizeof(lf))) 923 { 924 /* Another thread won the race. Use their cache instead of ours */ 925 list_remove(&sc->entry); 926 sc->refcount++; 927 LeaveCriticalSection(&cs_script_cache); 928 heap_free(*psc); 929 *psc = sc; 930 return S_OK; 931 } 932 } 933 LeaveCriticalSection(&cs_script_cache); 934 TRACE("<- %p\n", sc); 935 return S_OK; 936 } 937 938 static WCHAR mirror_char( WCHAR ch ) 939 { 940 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN; 941 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)]; 942 } 943 944 static DWORD decode_surrogate_pair(const WCHAR *str, unsigned int index, unsigned int end) 945 { 946 if (index < end-1 && IS_SURROGATE_PAIR(str[index],str[index+1])) 947 { 948 DWORD ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00); 949 TRACE("Surrogate Pair %x %x => %x\n",str[index], str[index+1], ch); 950 return ch; 951 } 952 return 0; 953 } 954 955 static int usp10_compare_script_range(const void *key, const void *value) 956 { 957 const struct usp10_script_range *range = value; 958 const DWORD *ch = key; 959 960 if (*ch < range->rangeFirst) 961 return -1; 962 if (*ch > range->rangeLast) 963 return 1; 964 return 0; 965 } 966 967 static enum usp10_script get_char_script(const WCHAR *str, unsigned int index, 968 unsigned int end, unsigned int *consumed) 969 { 970 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0}; 971 struct usp10_script_range *range; 972 WORD type = 0, type2 = 0; 973 DWORD ch; 974 975 *consumed = 1; 976 977 if (str[index] == 0xc || str[index] == 0x20 || str[index] == 0x202f) 978 return Script_CR; 979 980 /* These punctuation characters are separated out as Latin punctuation */ 981 if (strchrW(latin_punc,str[index])) 982 return Script_Punctuation2; 983 984 /* These chars are itemized as Punctuation by Windows */ 985 if (str[index] == 0x2212 || str[index] == 0x2044) 986 return Script_Punctuation; 987 988 /* Currency Symbols by Unicode point */ 989 switch (str[index]) 990 { 991 case 0x09f2: 992 case 0x09f3: return Script_Bengali_Currency; 993 case 0x0af1: return Script_Gujarati_Currency; 994 case 0x0e3f: return Script_Thai_Currency; 995 case 0x20aa: return Script_Hebrew_Currency; 996 case 0x20ab: return Script_Vietnamese_Currency; 997 case 0xfb29: return Script_Hebrew_Currency; 998 } 999 1000 GetStringTypeW(CT_CTYPE1, &str[index], 1, &type); 1001 GetStringTypeW(CT_CTYPE2, &str[index], 1, &type2); 1002 1003 if (type == 0) 1004 return SCRIPT_UNDEFINED; 1005 1006 if (type & C1_CNTRL) 1007 return Script_Control; 1008 1009 ch = decode_surrogate_pair(str, index, end); 1010 if (ch) 1011 *consumed = 2; 1012 else 1013 ch = str[index]; 1014 1015 if (!(range = bsearch(&ch, script_ranges, ARRAY_SIZE(script_ranges), 1016 sizeof(*script_ranges), usp10_compare_script_range))) 1017 return (*consumed == 2) ? Script_Surrogates : Script_Undefined; 1018 1019 if (range->numericScript && (type & C1_DIGIT || type2 == C2_ARABICNUMBER)) 1020 return range->numericScript; 1021 if (range->punctScript && type & C1_PUNCT) 1022 return range->punctScript; 1023 return range->script; 1024 } 1025 1026 static int compare_FindGlyph(const void *a, const void* b) 1027 { 1028 const FindGlyph_struct *find = (FindGlyph_struct*)a; 1029 const WORD *idx= (WORD*)b; 1030 int rc = 0; 1031 1032 if ( find->target > *idx) 1033 rc = 1; 1034 else if (find->target < *idx) 1035 rc = -1; 1036 1037 if (!find->ascending) 1038 rc *= -1; 1039 return rc; 1040 } 1041 1042 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target) 1043 { 1044 FindGlyph_struct fgs; 1045 WORD *ptr; 1046 INT k; 1047 1048 if (pwLogClust[0] < pwLogClust[cChars-1]) 1049 fgs.ascending = TRUE; 1050 else 1051 fgs.ascending = FALSE; 1052 1053 fgs.target = target; 1054 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph); 1055 1056 if (!ptr) 1057 return -1; 1058 1059 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--) 1060 ; 1061 k++; 1062 1063 return k; 1064 } 1065 1066 /*********************************************************************** 1067 * ScriptFreeCache (USP10.@) 1068 * 1069 * Free a script cache. 1070 * 1071 * PARAMS 1072 * psc [I/O] Script cache. 1073 * 1074 * RETURNS 1075 * Success: S_OK 1076 * Failure: Non-zero HRESULT value. 1077 */ 1078 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc) 1079 { 1080 TRACE("%p\n", psc); 1081 1082 if (psc && *psc) 1083 { 1084 unsigned int i; 1085 INT n; 1086 1087 EnterCriticalSection(&cs_script_cache); 1088 if (--((ScriptCache *)*psc)->refcount > 0) 1089 { 1090 LeaveCriticalSection(&cs_script_cache); 1091 *psc = NULL; 1092 return S_OK; 1093 } 1094 list_remove(&((ScriptCache *)*psc)->entry); 1095 LeaveCriticalSection(&cs_script_cache); 1096 1097 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++) 1098 { 1099 heap_free(((ScriptCache *)*psc)->widths[i]); 1100 } 1101 for (i = 0; i < NUM_PAGES; i++) 1102 { 1103 unsigned int j; 1104 if (((ScriptCache *)*psc)->page[i]) 1105 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++) 1106 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]); 1107 heap_free(((ScriptCache *)*psc)->page[i]); 1108 } 1109 heap_free(((ScriptCache *)*psc)->GSUB_Table); 1110 heap_free(((ScriptCache *)*psc)->GDEF_Table); 1111 heap_free(((ScriptCache *)*psc)->CMAP_Table); 1112 heap_free(((ScriptCache *)*psc)->GPOS_Table); 1113 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++) 1114 { 1115 int j; 1116 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++) 1117 { 1118 int k; 1119 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++) 1120 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups); 1121 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features); 1122 } 1123 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++) 1124 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups); 1125 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features); 1126 heap_free(((ScriptCache *)*psc)->scripts[n].languages); 1127 } 1128 heap_free(((ScriptCache *)*psc)->scripts); 1129 heap_free(((ScriptCache *)*psc)->otm); 1130 heap_free(*psc); 1131 *psc = NULL; 1132 } 1133 return S_OK; 1134 } 1135 1136 /*********************************************************************** 1137 * ScriptGetProperties (USP10.@) 1138 * 1139 * Retrieve a list of script properties. 1140 * 1141 * PARAMS 1142 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers. 1143 * num [I] Pointer to the number of scripts. 1144 * 1145 * RETURNS 1146 * Success: S_OK 1147 * Failure: Non-zero HRESULT value. 1148 * 1149 * NOTES 1150 * Behaviour matches WinXP. 1151 */ 1152 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num) 1153 { 1154 TRACE("(%p,%p)\n", props, num); 1155 1156 if (!props && !num) return E_INVALIDARG; 1157 1158 if (num) *num = ARRAY_SIZE(script_props); 1159 if (props) *props = script_props; 1160 1161 return S_OK; 1162 } 1163 1164 /*********************************************************************** 1165 * ScriptGetFontProperties (USP10.@) 1166 * 1167 * Get information on special glyphs. 1168 * 1169 * PARAMS 1170 * hdc [I] Device context. 1171 * psc [I/O] Opaque pointer to a script cache. 1172 * sfp [O] Font properties structure. 1173 */ 1174 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp) 1175 { 1176 HRESULT hr; 1177 1178 TRACE("%p,%p,%p\n", hdc, psc, sfp); 1179 1180 if (!sfp) return E_INVALIDARG; 1181 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 1182 1183 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES)) 1184 return E_INVALIDARG; 1185 1186 get_cache_font_properties(sfp, *psc); 1187 1188 return S_OK; 1189 } 1190 1191 /*********************************************************************** 1192 * ScriptRecordDigitSubstitution (USP10.@) 1193 * 1194 * Record digit substitution settings for a given locale. 1195 * 1196 * PARAMS 1197 * locale [I] Locale identifier. 1198 * sds [I] Structure to record substitution settings. 1199 * 1200 * RETURNS 1201 * Success: S_OK 1202 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise. 1203 * 1204 * SEE ALSO 1205 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx 1206 */ 1207 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds) 1208 { 1209 DWORD plgid, sub; 1210 1211 TRACE("0x%x, %p\n", locale, sds); 1212 1213 /* This implementation appears to be correct for all languages, but it's 1214 * not clear if sds->DigitSubstitute is ever set to anything except 1215 * CONTEXT or NONE in reality */ 1216 1217 if (!sds) return E_POINTER; 1218 1219 locale = ConvertDefaultLocale(locale); 1220 1221 if (!IsValidLocale(locale, LCID_INSTALLED)) 1222 return E_INVALIDARG; 1223 1224 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale)); 1225 sds->TraditionalDigitLanguage = plgid; 1226 1227 if (plgid == LANG_ARABIC || plgid == LANG_FARSI) 1228 sds->NationalDigitLanguage = plgid; 1229 else 1230 sds->NationalDigitLanguage = LANG_ENGLISH; 1231 1232 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, 1233 (WCHAR *)&sub, sizeof(sub) / sizeof(WCHAR))) 1234 return E_INVALIDARG; 1235 1236 switch (sub) 1237 { 1238 case 0: 1239 if (plgid == LANG_ARABIC || plgid == LANG_FARSI) 1240 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT; 1241 else 1242 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE; 1243 break; 1244 case 1: 1245 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE; 1246 break; 1247 case 2: 1248 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL; 1249 break; 1250 default: 1251 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL; 1252 break; 1253 } 1254 1255 sds->dwReserved = 0; 1256 return S_OK; 1257 } 1258 1259 /*********************************************************************** 1260 * ScriptApplyDigitSubstitution (USP10.@) 1261 * 1262 * Apply digit substitution settings. 1263 * 1264 * PARAMS 1265 * sds [I] Structure with recorded substitution settings. 1266 * sc [I] Script control structure. 1267 * ss [I] Script state structure. 1268 * 1269 * RETURNS 1270 * Success: S_OK 1271 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT. 1272 */ 1273 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds, 1274 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss) 1275 { 1276 SCRIPT_DIGITSUBSTITUTE psds; 1277 1278 TRACE("%p, %p, %p\n", sds, sc, ss); 1279 1280 if (!sc || !ss) return E_POINTER; 1281 if (!sds) 1282 { 1283 sds = &psds; 1284 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK) 1285 return E_INVALIDARG; 1286 } 1287 1288 sc->uDefaultLanguage = LANG_ENGLISH; 1289 sc->fContextDigits = 0; 1290 ss->fDigitSubstitute = 0; 1291 1292 switch (sds->DigitSubstitute) { 1293 case SCRIPT_DIGITSUBSTITUTE_CONTEXT: 1294 case SCRIPT_DIGITSUBSTITUTE_NATIONAL: 1295 case SCRIPT_DIGITSUBSTITUTE_NONE: 1296 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL: 1297 return S_OK; 1298 default: 1299 return E_INVALIDARG; 1300 } 1301 } 1302 1303 static inline BOOL is_indic(enum usp10_script script) 1304 { 1305 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric); 1306 } 1307 1308 static inline enum usp10_script base_indic(enum usp10_script script) 1309 { 1310 switch (script) 1311 { 1312 case Script_Devanagari: 1313 case Script_Devanagari_Numeric: return Script_Devanagari; 1314 case Script_Bengali: 1315 case Script_Bengali_Numeric: 1316 case Script_Bengali_Currency: return Script_Bengali; 1317 case Script_Gurmukhi: 1318 case Script_Gurmukhi_Numeric: return Script_Gurmukhi; 1319 case Script_Gujarati: 1320 case Script_Gujarati_Numeric: 1321 case Script_Gujarati_Currency: return Script_Gujarati; 1322 case Script_Oriya: 1323 case Script_Oriya_Numeric: return Script_Oriya; 1324 case Script_Tamil: 1325 case Script_Tamil_Numeric: return Script_Tamil; 1326 case Script_Telugu: 1327 case Script_Telugu_Numeric: return Script_Telugu; 1328 case Script_Kannada: 1329 case Script_Kannada_Numeric: return Script_Kannada; 1330 case Script_Malayalam: 1331 case Script_Malayalam_Numeric: return Script_Malayalam; 1332 default: 1333 return Script_Undefined; 1334 }; 1335 } 1336 1337 static BOOL script_is_numeric(enum usp10_script script) 1338 { 1339 return scriptInformation[script].props.fNumeric; 1340 } 1341 1342 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars, 1343 int cMaxItems, const SCRIPT_CONTROL *psControl, 1344 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems, 1345 OPENTYPE_TAG *pScriptTags, int *pcItems) 1346 { 1347 1348 #define Numeric_space 0x0020 1349 #define ZWSP 0x200B 1350 #define ZWNJ 0x200C 1351 #define ZWJ 0x200D 1352 1353 enum usp10_script last_indic = Script_Undefined; 1354 int cnt = 0, index = 0, str = 0; 1355 enum usp10_script New_Script = -1; 1356 int i; 1357 WORD *levels = NULL; 1358 WORD *layout_levels = NULL; 1359 WORD *overrides = NULL; 1360 WORD *strength = NULL; 1361 enum usp10_script *scripts; 1362 WORD baselevel = 0; 1363 WORD baselayout = 0; 1364 BOOL new_run; 1365 WORD layoutRTL = 0; 1366 BOOL forceLevels = FALSE; 1367 unsigned int consumed = 0; 1368 HRESULT res = E_OUTOFMEMORY; 1369 1370 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems, 1371 psControl, psState, pItems, pcItems); 1372 1373 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2) 1374 return E_INVALIDARG; 1375 1376 if (!(scripts = heap_alloc(cInChars * sizeof(*scripts)))) 1377 return E_OUTOFMEMORY; 1378 1379 for (i = 0; i < cInChars; i++) 1380 { 1381 if (!consumed) 1382 { 1383 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed); 1384 consumed --; 1385 } 1386 else 1387 { 1388 scripts[i] = scripts[i-1]; 1389 consumed --; 1390 } 1391 /* Devanagari danda (U+0964) and double danda (U+0965) are used for 1392 all Indic scripts */ 1393 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic != Script_Undefined) 1394 scripts[i] = last_indic; 1395 else if (is_indic(scripts[i])) 1396 last_indic = base_indic(scripts[i]); 1397 1398 /* Some unicode points : 1399 (Zero Width Space U+200B - Right-to-Left Mark U+200F) 1400 (Left Right Embed U+202A - Left Right Override U+202D) 1401 (Left Right Isolate U+2066 - Pop Directional Isolate U+2069) 1402 will force us into bidi mode */ 1403 if (!forceLevels && ((pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F) || 1404 (pwcInChars[i] >= 0x202A && pwcInChars[i] <= 0x202E) || 1405 (pwcInChars[i] >= 0x2066 && pwcInChars[i] <= 0x2069))) 1406 1407 forceLevels = TRUE; 1408 1409 /* Diacritical marks merge with other scripts */ 1410 if (scripts[i] == Script_Diacritical) 1411 { 1412 if (i > 0) 1413 { 1414 if (pScriptTags) 1415 scripts[i] = scripts[i-1]; 1416 else 1417 { 1418 int j; 1419 BOOL asian = FALSE; 1420 enum usp10_script first_script = scripts[i-1]; 1421 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--) 1422 { 1423 enum usp10_script original = scripts[j]; 1424 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo) 1425 { 1426 asian = TRUE; 1427 break; 1428 } 1429 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex) 1430 break; 1431 scripts[j] = scripts[i]; 1432 if (original == Script_Punctuation2) 1433 break; 1434 } 1435 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian)) 1436 scripts[i] = scripts[j]; 1437 } 1438 } 1439 } 1440 } 1441 1442 for (i = 0; i < cInChars; i++) 1443 { 1444 /* Joiners get merged preferencially right */ 1445 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP)) 1446 { 1447 int j; 1448 if (i+1 == cInChars) 1449 scripts[i] = scripts[i-1]; 1450 else 1451 { 1452 for (j = i+1; j < cInChars; j++) 1453 { 1454 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ 1455 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space) 1456 { 1457 scripts[i] = scripts[j]; 1458 break; 1459 } 1460 } 1461 } 1462 } 1463 } 1464 1465 if (psState && psControl) 1466 { 1467 levels = heap_alloc_zero(cInChars * sizeof(WORD)); 1468 if (!levels) 1469 goto nomemory; 1470 1471 overrides = heap_alloc_zero(cInChars * sizeof(WORD)); 1472 if (!overrides) 1473 goto nomemory; 1474 1475 layout_levels = heap_alloc_zero(cInChars * sizeof(WORD)); 1476 if (!layout_levels) 1477 goto nomemory; 1478 1479 if (psState->fOverrideDirection) 1480 { 1481 if (!forceLevels) 1482 { 1483 SCRIPT_STATE s = *psState; 1484 s.fOverrideDirection = FALSE; 1485 BIDI_DetermineLevels(pwcInChars, cInChars, &s, psControl, layout_levels, overrides); 1486 if (odd(layout_levels[0])) 1487 forceLevels = TRUE; 1488 else for (i = 0; i < cInChars; i++) 1489 if (layout_levels[i]!=layout_levels[0]) 1490 { 1491 forceLevels = TRUE; 1492 break; 1493 } 1494 } 1495 1496 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides); 1497 } 1498 else 1499 { 1500 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides); 1501 memcpy(layout_levels, levels, cInChars * sizeof(WORD)); 1502 } 1503 baselevel = levels[0]; 1504 baselayout = layout_levels[0]; 1505 for (i = 0; i < cInChars; i++) 1506 if (levels[i]!=levels[0]) 1507 break; 1508 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels) 1509 { 1510 heap_free(levels); 1511 heap_free(overrides); 1512 heap_free(layout_levels); 1513 overrides = NULL; 1514 levels = NULL; 1515 layout_levels = NULL; 1516 } 1517 else 1518 { 1519 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0}; 1520 static const WCHAR repeatable_math_punc[] = {'#','$','%','+','-','/',0x2212, 0x2044,0}; 1521 1522 strength = heap_alloc_zero(cInChars * sizeof(WORD)); 1523 if (!strength) 1524 goto nomemory; 1525 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength); 1526 1527 /* We currently mis-level leading Diacriticals */ 1528 if (scripts[0] == Script_Diacritical) 1529 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++) 1530 { 1531 levels[i] = odd(levels[i])?levels[i]+1:levels[i]; 1532 strength[i] = BIDI_STRONG; 1533 } 1534 1535 /* Math punctuation bordered on both sides by numbers can be 1536 merged into the number */ 1537 for (i = 0; i < cInChars; i++) 1538 { 1539 if (i > 0 && i < cInChars-1 && 1540 script_is_numeric(scripts[i-1]) && 1541 strchrW(math_punc, pwcInChars[i])) 1542 { 1543 if (script_is_numeric(scripts[i+1])) 1544 { 1545 scripts[i] = scripts[i+1]; 1546 levels[i] = levels[i-1]; 1547 strength[i] = strength[i-1]; 1548 i++; 1549 } 1550 else if (strchrW(repeatable_math_punc, pwcInChars[i])) 1551 { 1552 int j; 1553 for (j = i+1; j < cInChars; j++) 1554 { 1555 if (script_is_numeric(scripts[j])) 1556 { 1557 for(;i<j; i++) 1558 { 1559 scripts[i] = scripts[j]; 1560 levels[i] = levels[i-1]; 1561 strength[i] = strength[i-1]; 1562 } 1563 } 1564 else if (pwcInChars[i] != pwcInChars[j]) break; 1565 } 1566 } 1567 } 1568 } 1569 1570 for (i = 0; i < cInChars; i++) 1571 { 1572 /* Numerics at level 0 get bumped to level 2 */ 1573 if (!overrides[i] && (levels[i] == 0 || (odd(psState->uBidiLevel) 1574 && levels[i] == psState->uBidiLevel + 1)) && script_is_numeric(scripts[i])) 1575 { 1576 levels[i] = 2; 1577 } 1578 1579 /* Joiners get merged preferencially right */ 1580 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP)) 1581 { 1582 int j; 1583 if (i+1 == cInChars && levels[i-1] == levels[i]) 1584 strength[i] = strength[i-1]; 1585 else 1586 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++) 1587 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ 1588 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space) 1589 { 1590 strength[i] = strength[j]; 1591 break; 1592 } 1593 } 1594 } 1595 if (psControl->fMergeNeutralItems) 1596 { 1597 /* Merge the neutrals */ 1598 for (i = 0; i < cInChars; i++) 1599 { 1600 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK) 1601 { 1602 int j; 1603 for (j = i; j > 0; j--) 1604 { 1605 if (levels[i] != levels[j]) 1606 break; 1607 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK)) 1608 { 1609 scripts[i] = scripts[j]; 1610 strength[i] = strength[j]; 1611 break; 1612 } 1613 } 1614 } 1615 /* Try going the other way */ 1616 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK) 1617 { 1618 int j; 1619 for (j = i; j < cInChars; j++) 1620 { 1621 if (levels[i] != levels[j]) 1622 break; 1623 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK)) 1624 { 1625 scripts[i] = scripts[j]; 1626 strength[i] = strength[j]; 1627 break; 1628 } 1629 } 1630 } 1631 } 1632 } 1633 } 1634 } 1635 1636 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0])) 1637 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space)) 1638 cnt++; 1639 1640 if (cnt == cInChars) /* All Spaces */ 1641 { 1642 cnt = 0; 1643 New_Script = scripts[cnt]; 1644 } 1645 1646 pItems[index].iCharPos = 0; 1647 pItems[index].a = scriptInformation[scripts[cnt]].a; 1648 if (pScriptTags) 1649 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag; 1650 1651 if (strength && strength[cnt] == BIDI_STRONG) 1652 str = strength[cnt]; 1653 else if (strength) 1654 str = strength[0]; 1655 1656 cnt = 0; 1657 1658 if (levels) 1659 { 1660 if (strength[cnt] == BIDI_STRONG) 1661 layoutRTL = odd(layout_levels[cnt]); 1662 else 1663 layoutRTL = (psState->uBidiLevel || odd(layout_levels[cnt])); 1664 if (overrides) 1665 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0); 1666 pItems[index].a.fRTL = odd(levels[cnt]); 1667 if (script_is_numeric(pItems[index].a.eScript)) 1668 pItems[index].a.fLayoutRTL = layoutRTL; 1669 else 1670 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL; 1671 pItems[index].a.s.uBidiLevel = levels[cnt]; 1672 } 1673 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt])) 1674 { 1675 if (pItems[index].a.s.uBidiLevel != baselevel) 1676 pItems[index].a.s.fOverrideDirection = TRUE; 1677 layoutRTL = odd(baselayout); 1678 pItems[index].a.s.uBidiLevel = baselevel; 1679 pItems[index].a.fRTL = odd(baselevel); 1680 if (script_is_numeric(pItems[index].a.eScript)) 1681 pItems[index].a.fLayoutRTL = odd(baselayout); 1682 else 1683 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL; 1684 } 1685 1686 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n", 1687 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt, 1688 pItems[index].iCharPos); 1689 1690 for (cnt=1; cnt < cInChars; cnt++) 1691 { 1692 if(pwcInChars[cnt] != Numeric_space) 1693 New_Script = scripts[cnt]; 1694 else if (levels) 1695 { 1696 int j = 1; 1697 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j]) 1698 j++; 1699 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j]) 1700 New_Script = scripts[cnt+j]; 1701 else 1702 New_Script = scripts[cnt]; 1703 } 1704 1705 new_run = FALSE; 1706 /* merge space strengths*/ 1707 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript) 1708 str = BIDI_STRONG; 1709 1710 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript) 1711 str = BIDI_NEUTRAL; 1712 1713 /* changes in level */ 1714 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel)) 1715 { 1716 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]); 1717 new_run = TRUE; 1718 } 1719 /* changes in strength */ 1720 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt]) 1721 { 1722 TRACE("Strength break (%i/%i)\n",str,strength[cnt]); 1723 new_run = TRUE; 1724 } 1725 /* changes in script */ 1726 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control)) 1727 { 1728 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script); 1729 new_run = TRUE; 1730 } 1731 1732 if (!new_run && strength && str == BIDI_STRONG) 1733 { 1734 layoutRTL = odd(layout_levels[cnt]); 1735 if (script_is_numeric(pItems[index].a.eScript)) 1736 pItems[index].a.fLayoutRTL = layoutRTL; 1737 } 1738 1739 if (new_run) 1740 { 1741 TRACE("New_Level = %i, New_Strength = %i, New_Script=%d, eScript=%d\n", levels?levels[cnt]:-1, strength?strength[cnt]:str, New_Script, pItems[index].a.eScript); 1742 1743 index++; 1744 if (index+1 > cMaxItems) 1745 goto nomemory; 1746 1747 if (strength) 1748 str = strength[cnt]; 1749 1750 pItems[index].iCharPos = cnt; 1751 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS)); 1752 1753 pItems[index].a = scriptInformation[New_Script].a; 1754 if (pScriptTags) 1755 pScriptTags[index] = scriptInformation[New_Script].scriptTag; 1756 if (levels) 1757 { 1758 if (overrides) 1759 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0); 1760 if (layout_levels[cnt] == 0) 1761 layoutRTL = 0; 1762 else 1763 layoutRTL = (layoutRTL || odd(layout_levels[cnt])); 1764 pItems[index].a.fRTL = odd(levels[cnt]); 1765 if (script_is_numeric(pItems[index].a.eScript)) 1766 pItems[index].a.fLayoutRTL = layoutRTL; 1767 else 1768 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL; 1769 pItems[index].a.s.uBidiLevel = levels[cnt]; 1770 } 1771 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt])) 1772 { 1773 if (pItems[index].a.s.uBidiLevel != baselevel) 1774 pItems[index].a.s.fOverrideDirection = TRUE; 1775 pItems[index].a.s.uBidiLevel = baselevel; 1776 pItems[index].a.fRTL = odd(baselevel); 1777 if (script_is_numeric(pItems[index].a.eScript)) 1778 pItems[index].a.fLayoutRTL = layoutRTL; 1779 else 1780 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL; 1781 } 1782 1783 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos); 1784 } 1785 } 1786 1787 /* While not strictly necessary according to the spec, make sure the n+1 1788 * item is set up to prevent random behaviour if the caller erroneously 1789 * checks the n+1 structure */ 1790 index++; 1791 if (index + 1 > cMaxItems) goto nomemory; 1792 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS)); 1793 1794 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos); 1795 1796 /* Set one SCRIPT_STATE item being returned */ 1797 if (pcItems) *pcItems = index; 1798 1799 /* Set SCRIPT_ITEM */ 1800 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */ 1801 res = S_OK; 1802 nomemory: 1803 heap_free(levels); 1804 heap_free(overrides); 1805 heap_free(layout_levels); 1806 heap_free(strength); 1807 heap_free(scripts); 1808 return res; 1809 } 1810 1811 /*********************************************************************** 1812 * ScriptItemizeOpenType (USP10.@) 1813 * 1814 * Split a Unicode string into shapeable parts. 1815 * 1816 * PARAMS 1817 * pwcInChars [I] String to split. 1818 * cInChars [I] Number of characters in pwcInChars. 1819 * cMaxItems [I] Maximum number of items to return. 1820 * psControl [I] Pointer to a SCRIPT_CONTROL structure. 1821 * psState [I] Pointer to a SCRIPT_STATE structure. 1822 * pItems [O] Buffer to receive SCRIPT_ITEM structures. 1823 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs. 1824 * pcItems [O] Number of script items returned. 1825 * 1826 * RETURNS 1827 * Success: S_OK 1828 * Failure: Non-zero HRESULT value. 1829 */ 1830 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems, 1831 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState, 1832 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems) 1833 { 1834 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems); 1835 } 1836 1837 /*********************************************************************** 1838 * ScriptItemize (USP10.@) 1839 * 1840 * Split a Unicode string into shapeable parts. 1841 * 1842 * PARAMS 1843 * pwcInChars [I] String to split. 1844 * cInChars [I] Number of characters in pwcInChars. 1845 * cMaxItems [I] Maximum number of items to return. 1846 * psControl [I] Pointer to a SCRIPT_CONTROL structure. 1847 * psState [I] Pointer to a SCRIPT_STATE structure. 1848 * pItems [O] Buffer to receive SCRIPT_ITEM structures. 1849 * pcItems [O] Number of script items returned. 1850 * 1851 * RETURNS 1852 * Success: S_OK 1853 * Failure: Non-zero HRESULT value. 1854 */ 1855 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems, 1856 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState, 1857 SCRIPT_ITEM *pItems, int *pcItems) 1858 { 1859 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems); 1860 } 1861 1862 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x) 1863 { 1864 int defWidth; 1865 int cTabStops=0; 1866 INT *lpTabPos = NULL; 1867 INT nTabOrg = 0; 1868 INT x = 0; 1869 1870 if (pTabdef) 1871 lpTabPos = pTabdef->pTabStops; 1872 1873 if (pTabdef && pTabdef->iTabOrigin) 1874 { 1875 if (pTabdef->iScale) 1876 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4; 1877 else 1878 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth; 1879 } 1880 1881 if (pTabdef) 1882 cTabStops = pTabdef->cTabStops; 1883 1884 if (cTabStops == 1) 1885 { 1886 if (pTabdef->iScale) 1887 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4; 1888 else 1889 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth; 1890 cTabStops = 0; 1891 } 1892 else 1893 { 1894 if (pTabdef->iScale) 1895 defWidth = (32 * pTabdef->iScale) / 4; 1896 else 1897 defWidth = 8 * psc->tm.tmAveCharWidth; 1898 } 1899 1900 for (; cTabStops>0 ; lpTabPos++, cTabStops--) 1901 { 1902 int position = *lpTabPos; 1903 if (position < 0) 1904 position = -1 * position; 1905 if (pTabdef->iScale) 1906 position = (position * pTabdef->iScale) / 4; 1907 else 1908 position = position * psc->tm.tmAveCharWidth; 1909 1910 if( nTabOrg + position > current_x) 1911 { 1912 if( position >= 0) 1913 { 1914 /* a left aligned tab */ 1915 x = (nTabOrg + position) - current_x; 1916 break; 1917 } 1918 else 1919 { 1920 FIXME("Negative tabstop\n"); 1921 break; 1922 } 1923 } 1924 } 1925 if ((!cTabStops) && (defWidth > 0)) 1926 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x; 1927 else if ((!cTabStops) && (defWidth < 0)) 1928 FIXME("TODO: Negative defWidth\n"); 1929 1930 return x; 1931 } 1932 1933 /*********************************************************************** 1934 * Helper function for ScriptStringAnalyse 1935 */ 1936 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, 1937 const WCHAR *pwcInChars, int cChars ) 1938 { 1939 /* FIXME: When to properly fallback is still a bit of a mystery */ 1940 WORD *glyphs; 1941 1942 if (psa->fNoGlyphIndex) 1943 return FALSE; 1944 1945 if (init_script_cache(hdc, psc) != S_OK) 1946 return FALSE; 1947 1948 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK) 1949 return TRUE; 1950 1951 glyphs = heap_alloc(sizeof(WORD) * cChars); 1952 if (!glyphs) 1953 return FALSE; 1954 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK) 1955 { 1956 heap_free(glyphs); 1957 return TRUE; 1958 } 1959 heap_free(glyphs); 1960 1961 return FALSE; 1962 } 1963 1964 static void find_fallback_font(enum usp10_script scriptid, WCHAR *FaceName) 1965 { 1966 HKEY hkey; 1967 1968 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey)) 1969 { 1970 static const WCHAR szFmt[] = {'%','x',0}; 1971 WCHAR value[10]; 1972 DWORD count = LF_FACESIZE * sizeof(WCHAR); 1973 DWORD type; 1974 1975 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag); 1976 if (RegQueryValueExW(hkey, value, 0, &type, (BYTE *)FaceName, &count)) 1977 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont); 1978 RegCloseKey(hkey); 1979 } 1980 else 1981 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont); 1982 } 1983 1984 /*********************************************************************** 1985 * ScriptStringAnalyse (USP10.@) 1986 * 1987 */ 1988 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString, 1989 int cGlyphs, int iCharset, DWORD dwFlags, 1990 int iReqWidth, SCRIPT_CONTROL *psControl, 1991 SCRIPT_STATE *psState, const int *piDx, 1992 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass, 1993 SCRIPT_STRING_ANALYSIS *pssa) 1994 { 1995 HRESULT hr = E_OUTOFMEMORY; 1996 StringAnalysis *analysis = NULL; 1997 SCRIPT_CONTROL sControl; 1998 SCRIPT_STATE sState; 1999 int i, num_items = 255; 2000 BYTE *BidiLevel; 2001 WCHAR *iString = NULL; 2002 2003 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n", 2004 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth, 2005 psControl, psState, piDx, pTabdef, pbInClass, pssa); 2006 2007 if (iCharset != -1) 2008 { 2009 FIXME("Only Unicode strings are supported\n"); 2010 return E_INVALIDARG; 2011 } 2012 if (cString < 1 || !pString) return E_INVALIDARG; 2013 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING; 2014 2015 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY; 2016 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error; 2017 2018 /* FIXME: handle clipping */ 2019 analysis->clip_len = cString; 2020 analysis->hdc = hdc; 2021 analysis->ssa_flags = dwFlags; 2022 2023 if (psState) 2024 sState = *psState; 2025 else 2026 memset(&sState, 0, sizeof(SCRIPT_STATE)); 2027 2028 if (psControl) 2029 sControl = *psControl; 2030 else 2031 memset(&sControl, 0, sizeof(SCRIPT_CONTROL)); 2032 2033 if (dwFlags & SSA_PASSWORD) 2034 { 2035 iString = heap_alloc(sizeof(WCHAR)*cString); 2036 if (!iString) 2037 { 2038 hr = E_OUTOFMEMORY; 2039 goto error; 2040 } 2041 for (i = 0; i < cString; i++) 2042 iString[i] = *((const WCHAR *)pString); 2043 pString = iString; 2044 } 2045 2046 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem, 2047 &analysis->numItems); 2048 2049 if (FAILED(hr)) 2050 { 2051 if (hr == E_OUTOFMEMORY) 2052 hr = E_INVALIDARG; 2053 goto error; 2054 } 2055 2056 /* set back to out of memory for default goto error behaviour */ 2057 hr = E_OUTOFMEMORY; 2058 2059 if (dwFlags & SSA_BREAK) 2060 { 2061 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString))) 2062 { 2063 for (i = 0; i < analysis->numItems; i++) 2064 ScriptBreak(&((WCHAR *)pString)[analysis->pItem[i].iCharPos], 2065 analysis->pItem[i + 1].iCharPos - analysis->pItem[i].iCharPos, 2066 &analysis->pItem[i].a, &analysis->logattrs[analysis->pItem[i].iCharPos]); 2067 } 2068 else 2069 goto error; 2070 } 2071 2072 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems))) 2073 goto error; 2074 if (!(BidiLevel = heap_alloc_zero(analysis->numItems))) 2075 goto error; 2076 2077 if (dwFlags & SSA_GLYPHS) 2078 { 2079 int tab_x = 0; 2080 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems))) 2081 { 2082 heap_free(BidiLevel); 2083 goto error; 2084 } 2085 2086 for (i = 0; i < analysis->numItems; i++) 2087 { 2088 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc; 2089 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos; 2090 int numGlyphs = 1.5 * cChar + 16; 2091 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs); 2092 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar); 2093 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs); 2094 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs); 2095 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs); 2096 int numGlyphsReturned; 2097 HFONT originalFont = 0x0; 2098 2099 /* FIXME: non unicode strings */ 2100 const WCHAR* pStr = (const WCHAR*)pString; 2101 analysis->glyphs[i].fallbackFont = NULL; 2102 2103 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset) 2104 { 2105 heap_free (BidiLevel); 2106 heap_free (glyphs); 2107 heap_free (pwLogClust); 2108 heap_free (piAdvance); 2109 heap_free (psva); 2110 heap_free (pGoffset); 2111 hr = E_OUTOFMEMORY; 2112 goto error; 2113 } 2114 2115 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar)) 2116 { 2117 LOGFONTW lf; 2118 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf); 2119 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet; 2120 lf.lfFaceName[0] = 0; 2121 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName); 2122 if (lf.lfFaceName[0]) 2123 { 2124 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf); 2125 if (analysis->glyphs[i].fallbackFont) 2126 { 2127 ScriptFreeCache(sc); 2128 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont); 2129 } 2130 } 2131 } 2132 2133 /* FIXME: When we properly shape Hangul remove this check */ 2134 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul) 2135 analysis->pItem[i].a.fNoGlyphIndex = TRUE; 2136 2137 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL) 2138 analysis->pItem[i].a.fNoGlyphIndex = TRUE; 2139 2140 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs, 2141 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned); 2142 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a, 2143 piAdvance, pGoffset, &analysis->glyphs[i].abc); 2144 if (originalFont) 2145 SelectObject(hdc,originalFont); 2146 2147 if (dwFlags & SSA_TAB) 2148 { 2149 int tabi = 0; 2150 for (tabi = 0; tabi < cChar; tabi++) 2151 { 2152 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009) 2153 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x); 2154 tab_x+=piAdvance[tabi]; 2155 } 2156 } 2157 2158 analysis->glyphs[i].numGlyphs = numGlyphsReturned; 2159 analysis->glyphs[i].glyphs = glyphs; 2160 analysis->glyphs[i].pwLogClust = pwLogClust; 2161 analysis->glyphs[i].piAdvance = piAdvance; 2162 analysis->glyphs[i].psva = psva; 2163 analysis->glyphs[i].pGoffset = pGoffset; 2164 analysis->glyphs[i].iMaxPosX= -1; 2165 2166 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel; 2167 } 2168 } 2169 else 2170 { 2171 for (i = 0; i < analysis->numItems; i++) 2172 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel; 2173 } 2174 2175 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual); 2176 heap_free(BidiLevel); 2177 2178 *pssa = analysis; 2179 heap_free(iString); 2180 return S_OK; 2181 2182 error: 2183 heap_free(iString); 2184 heap_free(analysis->glyphs); 2185 heap_free(analysis->logattrs); 2186 heap_free(analysis->pItem); 2187 heap_free(analysis->logical2visual); 2188 heap_free(analysis); 2189 return hr; 2190 } 2191 2192 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction) 2193 { 2194 if (pva[glyph].fClusterStart) 2195 return TRUE; 2196 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0) 2197 return TRUE; 2198 2199 return FALSE; 2200 } 2201 2202 2203 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa, 2204 int iX, 2205 int iY, 2206 int iItem, 2207 int cStart, 2208 int cEnd, 2209 UINT uOptions, 2210 const RECT *prc, 2211 BOOL fSelected, 2212 BOOL fDisabled) 2213 { 2214 StringAnalysis *analysis; 2215 int off_x = 0; 2216 HRESULT hr; 2217 COLORREF BkColor = 0x0; 2218 COLORREF TextColor = 0x0; 2219 INT BkMode = 0; 2220 INT runStart, runEnd; 2221 INT iGlyph, cGlyphs; 2222 HFONT oldFont = 0x0; 2223 RECT crc; 2224 int i; 2225 2226 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n", 2227 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled); 2228 2229 if (!(analysis = ssa)) return E_INVALIDARG; 2230 2231 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) || 2232 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd)) 2233 return S_OK; 2234 2235 CopyRect(&crc,prc); 2236 if (fSelected) 2237 { 2238 BkMode = GetBkMode(analysis->hdc); 2239 SetBkMode( analysis->hdc, OPAQUE); 2240 BkColor = GetBkColor(analysis->hdc); 2241 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT)); 2242 if (!fDisabled) 2243 { 2244 TextColor = GetTextColor(analysis->hdc); 2245 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 2246 } 2247 } 2248 if (analysis->glyphs[iItem].fallbackFont) 2249 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont); 2250 2251 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart) 2252 runStart = cStart - analysis->pItem[iItem].iCharPos; 2253 else 2254 runStart = 0; 2255 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd) 2256 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos; 2257 else 2258 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1; 2259 2260 if (analysis->pItem[iItem].a.fRTL) 2261 { 2262 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos) 2263 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x); 2264 else 2265 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x); 2266 crc.left = iX + off_x; 2267 } 2268 else 2269 { 2270 if (cStart >=0 && runStart) 2271 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x); 2272 else 2273 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x); 2274 crc.left = iX + off_x; 2275 } 2276 2277 if (analysis->pItem[iItem].a.fRTL) 2278 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd]; 2279 else 2280 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart]; 2281 2282 if (analysis->pItem[iItem].a.fRTL) 2283 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph; 2284 else 2285 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph; 2286 2287 cGlyphs++; 2288 2289 /* adjust for cluster glyphs when starting */ 2290 if (analysis->pItem[iItem].a.fRTL) 2291 i = analysis->pItem[iItem+1].iCharPos - 1; 2292 else 2293 i = analysis->pItem[iItem].iCharPos; 2294 2295 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++) 2296 { 2297 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph) 2298 { 2299 if (analysis->pItem[iItem].a.fRTL) 2300 ScriptStringCPtoX(ssa, i, TRUE, &off_x); 2301 else 2302 ScriptStringCPtoX(ssa, i, FALSE, &off_x); 2303 break; 2304 } 2305 } 2306 2307 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo) 2308 { 2309 INT direction; 2310 INT clust_glyph; 2311 2312 clust_glyph = iGlyph + cGlyphs; 2313 if (analysis->pItem[iItem].a.fRTL) 2314 direction = -1; 2315 else 2316 direction = 1; 2317 2318 while(clust_glyph < analysis->glyphs[iItem].numGlyphs && 2319 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction)) 2320 { 2321 cGlyphs++; 2322 clust_glyph++; 2323 } 2324 } 2325 2326 hr = ScriptTextOut(analysis->hdc, 2327 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x, 2328 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0, 2329 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs, 2330 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL, 2331 &analysis->glyphs[iItem].pGoffset[iGlyph]); 2332 2333 TRACE("ScriptTextOut hr=%08x\n", hr); 2334 2335 if (fSelected) 2336 { 2337 SetBkColor(analysis->hdc, BkColor); 2338 SetBkMode( analysis->hdc, BkMode); 2339 if (!fDisabled) 2340 SetTextColor(analysis->hdc, TextColor); 2341 } 2342 if (analysis->glyphs[iItem].fallbackFont) 2343 SelectObject(analysis->hdc, oldFont); 2344 2345 return hr; 2346 } 2347 2348 /*********************************************************************** 2349 * ScriptStringOut (USP10.@) 2350 * 2351 * This function takes the output of ScriptStringAnalyse and joins the segments 2352 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut 2353 * only processes glyphs. 2354 * 2355 * Parameters: 2356 * ssa [I] buffer to hold the analysed string components 2357 * iX [I] X axis displacement for output 2358 * iY [I] Y axis displacement for output 2359 * uOptions [I] flags controlling output processing 2360 * prc [I] rectangle coordinates 2361 * iMinSel [I] starting pos for substringing output string 2362 * iMaxSel [I] ending pos for substringing output string 2363 * fDisabled [I] controls text highlighting 2364 * 2365 * RETURNS 2366 * Success: S_OK 2367 * Failure: is the value returned by ScriptTextOut 2368 */ 2369 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa, 2370 int iX, 2371 int iY, 2372 UINT uOptions, 2373 const RECT *prc, 2374 int iMinSel, 2375 int iMaxSel, 2376 BOOL fDisabled) 2377 { 2378 StringAnalysis *analysis; 2379 int item; 2380 HRESULT hr; 2381 2382 TRACE("(%p,%d,%d,0x%08x,%s,%d,%d,%d)\n", 2383 ssa, iX, iY, uOptions, wine_dbgstr_rect(prc), iMinSel, iMaxSel, fDisabled); 2384 2385 if (!(analysis = ssa)) return E_INVALIDARG; 2386 if (!(analysis->ssa_flags & SSA_GLYPHS)) return E_INVALIDARG; 2387 2388 for (item = 0; item < analysis->numItems; item++) 2389 { 2390 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled); 2391 if (FAILED(hr)) 2392 return hr; 2393 } 2394 2395 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0)) 2396 { 2397 if (iMaxSel > 0 && iMinSel < 0) 2398 iMinSel = 0; 2399 for (item = 0; item < analysis->numItems; item++) 2400 { 2401 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled); 2402 if (FAILED(hr)) 2403 return hr; 2404 } 2405 } 2406 2407 return S_OK; 2408 } 2409 2410 /*********************************************************************** 2411 * ScriptStringCPtoX (USP10.@) 2412 * 2413 */ 2414 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX) 2415 { 2416 int item; 2417 int runningX = 0; 2418 StringAnalysis* analysis = ssa; 2419 2420 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX); 2421 2422 if (!ssa || !pX) return S_FALSE; 2423 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE; 2424 2425 /* icp out of range */ 2426 if(icp < 0) 2427 { 2428 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_INVALID; 2429 return E_INVALIDARG; 2430 } 2431 2432 for(item=0; item<analysis->numItems; item++) 2433 { 2434 int CP, i; 2435 int offset; 2436 2437 i = analysis->logical2visual[item]; 2438 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos; 2439 /* initialize max extents for uninitialized runs */ 2440 if (analysis->glyphs[i].iMaxPosX == -1) 2441 { 2442 if (analysis->pItem[i].a.fRTL) 2443 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust, 2444 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance, 2445 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX); 2446 else 2447 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust, 2448 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance, 2449 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX); 2450 } 2451 2452 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos) 2453 { 2454 runningX += analysis->glyphs[i].iMaxPosX; 2455 continue; 2456 } 2457 2458 icp -= analysis->pItem[i].iCharPos; 2459 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust, 2460 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance, 2461 &analysis->pItem[i].a, &offset); 2462 runningX += offset; 2463 2464 *pX = runningX; 2465 return S_OK; 2466 } 2467 2468 /* icp out of range */ 2469 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_INVALID; 2470 return E_INVALIDARG; 2471 } 2472 2473 /*********************************************************************** 2474 * ScriptStringXtoCP (USP10.@) 2475 * 2476 */ 2477 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing) 2478 { 2479 StringAnalysis* analysis = ssa; 2480 int item; 2481 2482 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing); 2483 2484 if (!ssa || !piCh || !piTrailing) return S_FALSE; 2485 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE; 2486 2487 /* out of range */ 2488 if(iX < 0) 2489 { 2490 if (analysis->pItem[0].a.fRTL) 2491 { 2492 *piCh = 1; 2493 *piTrailing = FALSE; 2494 } 2495 else 2496 { 2497 *piCh = -1; 2498 *piTrailing = TRUE; 2499 } 2500 return S_OK; 2501 } 2502 2503 for(item=0; item<analysis->numItems; item++) 2504 { 2505 int i; 2506 int CP; 2507 2508 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++) 2509 /* nothing */; 2510 2511 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos; 2512 /* initialize max extents for uninitialized runs */ 2513 if (analysis->glyphs[i].iMaxPosX == -1) 2514 { 2515 if (analysis->pItem[i].a.fRTL) 2516 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust, 2517 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance, 2518 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX); 2519 else 2520 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust, 2521 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance, 2522 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX); 2523 } 2524 2525 if (iX > analysis->glyphs[i].iMaxPosX) 2526 { 2527 iX -= analysis->glyphs[i].iMaxPosX; 2528 continue; 2529 } 2530 2531 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust, 2532 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance, 2533 &analysis->pItem[i].a, piCh, piTrailing); 2534 *piCh += analysis->pItem[i].iCharPos; 2535 2536 return S_OK; 2537 } 2538 2539 /* out of range */ 2540 *piCh = analysis->pItem[analysis->numItems].iCharPos; 2541 *piTrailing = FALSE; 2542 2543 return S_OK; 2544 } 2545 2546 2547 /*********************************************************************** 2548 * ScriptStringFree (USP10.@) 2549 * 2550 * Free a string analysis. 2551 * 2552 * PARAMS 2553 * pssa [I] string analysis. 2554 * 2555 * RETURNS 2556 * Success: S_OK 2557 * Failure: Non-zero HRESULT value. 2558 */ 2559 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa) 2560 { 2561 StringAnalysis* analysis; 2562 BOOL invalid; 2563 int i; 2564 2565 TRACE("(%p)\n", pssa); 2566 2567 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG; 2568 2569 invalid = analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_INVALID; 2570 2571 if (analysis->glyphs) 2572 { 2573 for (i = 0; i < analysis->numItems; i++) 2574 { 2575 heap_free(analysis->glyphs[i].glyphs); 2576 heap_free(analysis->glyphs[i].pwLogClust); 2577 heap_free(analysis->glyphs[i].piAdvance); 2578 heap_free(analysis->glyphs[i].psva); 2579 heap_free(analysis->glyphs[i].pGoffset); 2580 if (analysis->glyphs[i].fallbackFont) 2581 DeleteObject(analysis->glyphs[i].fallbackFont); 2582 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc); 2583 heap_free(analysis->glyphs[i].sc); 2584 } 2585 heap_free(analysis->glyphs); 2586 } 2587 2588 heap_free(analysis->pItem); 2589 heap_free(analysis->logattrs); 2590 heap_free(analysis->logical2visual); 2591 heap_free(analysis); 2592 2593 if (invalid) return E_INVALIDARG; 2594 return S_OK; 2595 } 2596 2597 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item, 2598 int direction, int* iCluster, int *check_out) 2599 { 2600 int clust_size = 1; 2601 int check; 2602 WORD clust = pwLogClust[item]; 2603 2604 for (check = item+direction; check < cChars && check >= 0; check+=direction) 2605 { 2606 if (pwLogClust[check] == clust) 2607 { 2608 clust_size ++; 2609 if (iCluster && *iCluster == -1) 2610 *iCluster = item; 2611 } 2612 else break; 2613 } 2614 2615 if (check_out) 2616 *check_out = check; 2617 2618 return clust_size; 2619 } 2620 2621 static inline int get_glyph_cluster_advance(const int* piAdvance, const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cGlyphs, int cChars, int glyph, int direction) 2622 { 2623 int advance; 2624 int log_clust_max; 2625 2626 advance = piAdvance[glyph]; 2627 2628 if (pwLogClust[0] > pwLogClust[cChars-1]) 2629 log_clust_max = pwLogClust[0]; 2630 else 2631 log_clust_max = pwLogClust[cChars-1]; 2632 2633 if (glyph > log_clust_max) 2634 return advance; 2635 2636 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction) 2637 { 2638 2639 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction)) 2640 break; 2641 if (glyph > log_clust_max) 2642 break; 2643 advance += piAdvance[glyph]; 2644 } 2645 2646 return advance; 2647 } 2648 2649 /*********************************************************************** 2650 * ScriptCPtoX (USP10.@) 2651 * 2652 */ 2653 HRESULT WINAPI ScriptCPtoX(int iCP, 2654 BOOL fTrailing, 2655 int cChars, 2656 int cGlyphs, 2657 const WORD *pwLogClust, 2658 const SCRIPT_VISATTR *psva, 2659 const int *piAdvance, 2660 const SCRIPT_ANALYSIS *psa, 2661 int *piX) 2662 { 2663 int item; 2664 float iPosX; 2665 int iSpecial = -1; 2666 int iCluster = -1; 2667 int clust_size = 1; 2668 float special_size = 0.0; 2669 int iMaxPos = 0; 2670 int advance = 0; 2671 BOOL rtl = FALSE; 2672 2673 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n", 2674 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, 2675 psa, piX); 2676 2677 if (psa->fRTL && ! psa->fLogicalOrder) 2678 rtl = TRUE; 2679 2680 if (fTrailing) 2681 iCP++; 2682 2683 if (rtl) 2684 { 2685 int max_clust = pwLogClust[0]; 2686 2687 for (item=0; item < cGlyphs; item++) 2688 if (pwLogClust[item] > max_clust) 2689 { 2690 ERR("We do not handle non reversed clusters properly\n"); 2691 break; 2692 } 2693 2694 iMaxPos = 0; 2695 for (item = max_clust; item >=0; item --) 2696 iMaxPos += piAdvance[item]; 2697 } 2698 2699 iPosX = 0.0; 2700 for (item=0; item < iCP && item < cChars; item++) 2701 { 2702 if (iSpecial == -1 && (iCluster == -1 || iCluster+clust_size <= item)) 2703 { 2704 int check; 2705 int clust = pwLogClust[item]; 2706 2707 iCluster = -1; 2708 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster, 2709 &check); 2710 2711 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1); 2712 2713 if (check >= cChars && !iMaxPos) 2714 { 2715 int glyph; 2716 for (glyph = clust; glyph < cGlyphs; glyph++) 2717 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1); 2718 iSpecial = item; 2719 special_size /= (cChars - item); 2720 iPosX += special_size; 2721 } 2722 else 2723 { 2724 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo) 2725 { 2726 clust_size --; 2727 if (clust_size == 0) 2728 iPosX += advance; 2729 } 2730 else 2731 iPosX += advance / (float)clust_size; 2732 } 2733 } 2734 else if (iSpecial != -1) 2735 iPosX += special_size; 2736 else /* (iCluster != -1) */ 2737 { 2738 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1); 2739 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo) 2740 { 2741 clust_size --; 2742 if (clust_size == 0) 2743 iPosX += adv; 2744 } 2745 else 2746 iPosX += adv / (float)clust_size; 2747 } 2748 } 2749 2750 if (iMaxPos > 0) 2751 { 2752 iPosX = iMaxPos - iPosX; 2753 if (iPosX < 0) 2754 iPosX = 0; 2755 } 2756 2757 *piX = iPosX; 2758 TRACE("*piX=%d\n", *piX); 2759 return S_OK; 2760 } 2761 2762 /* Count the number of characters in a cluster and its starting index*/ 2763 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index) 2764 { 2765 int size = 0; 2766 int i; 2767 2768 for (i = 0; i < cChars; i++) 2769 { 2770 if (pwLogClust[i] == cluster_index) 2771 { 2772 if (!size && start_index) 2773 { 2774 *start_index = i; 2775 if (!cluster_size) 2776 return TRUE; 2777 } 2778 size++; 2779 } 2780 else if (size) break; 2781 } 2782 if (cluster_size) 2783 *cluster_size = size; 2784 2785 return (size > 0); 2786 } 2787 2788 /* 2789 To handle multi-glyph clusters we need to find all the glyphs that are 2790 represented in the cluster. This involves finding the glyph whose 2791 index is the cluster index as well as whose glyph indices are greater than 2792 our cluster index but not part of a new cluster. 2793 2794 Then we sum all those glyphs' advances. 2795 */ 2796 static inline int get_cluster_advance(const int* piAdvance, 2797 const SCRIPT_VISATTR *psva, 2798 const WORD *pwLogClust, int cGlyphs, 2799 int cChars, int cluster, int direction) 2800 { 2801 int glyph_start; 2802 int glyph_end; 2803 int i, advance; 2804 2805 if (direction > 0) 2806 i = 0; 2807 else 2808 i = (cChars - 1); 2809 2810 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction) 2811 { 2812 if (glyph_start < 0 && pwLogClust[i] != cluster) continue; 2813 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i]; 2814 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i]; 2815 } 2816 if (glyph_end < 0) 2817 { 2818 if (direction > 0) 2819 glyph_end = cGlyphs; 2820 else 2821 { 2822 /* Don't fully understand multi-glyph reversed clusters yet, 2823 * do they occur for real or just in our test? */ 2824 FIXME("multi-glyph reversed clusters found\n"); 2825 glyph_end = glyph_start + 1; 2826 } 2827 } 2828 2829 /* Check for fClusterStart, finding this generally would mean a malformed set of data */ 2830 for (i = glyph_start+1; i< glyph_end; i++) 2831 { 2832 if (psva[i].fClusterStart) 2833 { 2834 glyph_end = i; 2835 break; 2836 } 2837 } 2838 2839 for (advance = 0, i = glyph_start; i < glyph_end; i++) 2840 advance += piAdvance[i]; 2841 2842 return advance; 2843 } 2844 2845 2846 /*********************************************************************** 2847 * ScriptXtoCP (USP10.@) 2848 * 2849 * Basic algorithm : 2850 * Use piAdvance to find the cluster we are looking at. 2851 * Find the character that is the first character of the cluster. 2852 * That is our base piCP. 2853 * If the script snaps to cluster boundaries (Hebrew, Indic, Thai) then we 2854 * are good. Otherwise if the cluster is larger than 1 glyph we need to 2855 * determine how far through the cluster to advance the cursor. 2856 */ 2857 HRESULT WINAPI ScriptXtoCP(int iX, 2858 int cChars, 2859 int cGlyphs, 2860 const WORD *pwLogClust, 2861 const SCRIPT_VISATTR *psva, 2862 const int *piAdvance, 2863 const SCRIPT_ANALYSIS *psa, 2864 int *piCP, 2865 int *piTrailing) 2866 { 2867 int direction = 1; 2868 int iPosX; 2869 int i; 2870 int glyph_index, cluster_index; 2871 int cluster_size; 2872 2873 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n", 2874 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, 2875 psa, piCP, piTrailing); 2876 2877 if (psa->fRTL && ! psa->fLogicalOrder) 2878 direction = -1; 2879 2880 /* Handle an iX < 0 */ 2881 if (iX < 0) 2882 { 2883 if (direction < 0) 2884 { 2885 *piCP = cChars; 2886 *piTrailing = 0; 2887 } 2888 else 2889 { 2890 *piCP = -1; 2891 *piTrailing = 1; 2892 } 2893 return S_OK; 2894 } 2895 2896 /* Looking for non-reversed clusters in a reversed string */ 2897 if (direction < 0) 2898 { 2899 int max_clust = pwLogClust[0]; 2900 for (i=0; i< cChars; i++) 2901 if (pwLogClust[i] > max_clust) 2902 { 2903 FIXME("We do not handle non reversed clusters properly\n"); 2904 break; 2905 } 2906 } 2907 2908 /* find the glyph_index based in iX */ 2909 if (direction > 0) 2910 { 2911 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++) 2912 ; 2913 } 2914 else 2915 { 2916 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++) 2917 ; 2918 } 2919 2920 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs); 2921 2922 *piTrailing = 0; 2923 if (glyph_index >= 0 && glyph_index < cGlyphs) 2924 { 2925 /* find the cluster */ 2926 if (direction > 0 ) 2927 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++]) 2928 ; 2929 else 2930 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++]) 2931 ; 2932 2933 TRACE("cluster_index %i\n", cluster_index); 2934 2935 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index) 2936 { 2937 /* We are off the end of the string */ 2938 *piCP = -1; 2939 *piTrailing = 1; 2940 return S_OK; 2941 } 2942 2943 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i); 2944 2945 TRACE("first char index %i\n",i); 2946 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo) 2947 { 2948 /* Check trailing */ 2949 if (glyph_index != cluster_index || 2950 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) || 2951 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2))) 2952 *piTrailing = cluster_size; 2953 } 2954 else 2955 { 2956 if (cluster_size > 1) 2957 { 2958 /* Be part way through the glyph cluster based on size and position */ 2959 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction); 2960 double cluster_part_width = cluster_advance / (float)cluster_size; 2961 double adv; 2962 int part_index; 2963 2964 /* back up to the beginning of the cluster */ 2965 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++) 2966 adv += piAdvance[part_index]; 2967 if (adv > iX) adv = iX; 2968 2969 TRACE("Multi-char cluster, no snap\n"); 2970 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv); 2971 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width); 2972 if (direction > 0) 2973 { 2974 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++) 2975 ; 2976 if (part_index) part_index--; 2977 } 2978 else 2979 { 2980 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++) 2981 ; 2982 if (part_index > cluster_size) 2983 { 2984 adv += cluster_part_width; 2985 part_index=cluster_size; 2986 } 2987 } 2988 2989 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv); 2990 2991 if (direction > 0) 2992 i += part_index; 2993 else 2994 i += (cluster_size - part_index); 2995 2996 /* Check trailing */ 2997 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) || 2998 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0))) 2999 *piTrailing = 1; 3000 } 3001 else 3002 { 3003 /* Check trailing */ 3004 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) || 3005 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2))) 3006 *piTrailing = 1; 3007 } 3008 } 3009 } 3010 else 3011 { 3012 TRACE("Point falls outside of string\n"); 3013 if (glyph_index < 0) 3014 i = cChars-1; 3015 else /* (glyph_index >= cGlyphs) */ 3016 i = cChars; 3017 3018 /* If not snaping in the reverse direction (such as Hebrew) Then 0 3019 point flow to the next character */ 3020 if (direction < 0) 3021 { 3022 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index]) 3023 i++; 3024 else 3025 *piTrailing = 1; 3026 } 3027 } 3028 3029 *piCP = i; 3030 3031 TRACE("*piCP=%d\n", *piCP); 3032 TRACE("*piTrailing=%d\n", *piTrailing); 3033 return S_OK; 3034 } 3035 3036 /*********************************************************************** 3037 * ScriptBreak (USP10.@) 3038 * 3039 * Retrieve line break information. 3040 * 3041 * PARAMS 3042 * chars [I] Array of characters. 3043 * sa [I] Script analysis. 3044 * la [I] Array of logical attribute structures. 3045 * 3046 * RETURNS 3047 * Success: S_OK 3048 * Failure: S_FALSE 3049 */ 3050 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la) 3051 { 3052 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la); 3053 3054 if (count < 0 || !la) return E_INVALIDARG; 3055 if (count == 0) return E_FAIL; 3056 3057 BREAK_line(chars, count, sa, la); 3058 3059 return S_OK; 3060 } 3061 3062 /*********************************************************************** 3063 * ScriptIsComplex (USP10.@) 3064 * 3065 * Determine if a string is complex. 3066 * 3067 * PARAMS 3068 * chars [I] Array of characters to test. 3069 * len [I] Length in characters. 3070 * flag [I] Flag. 3071 * 3072 * RETURNS 3073 * Success: S_OK 3074 * Failure: S_FALSE 3075 * 3076 */ 3077 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag) 3078 { 3079 enum usp10_script script; 3080 unsigned int i, consumed; 3081 3082 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag); 3083 3084 if (!chars || len < 0) 3085 return E_INVALIDARG; 3086 3087 for (i = 0; i < len; i+=consumed) 3088 { 3089 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39) 3090 return S_OK; 3091 3092 script = get_char_script(chars,i,len, &consumed); 3093 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))|| 3094 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL))) 3095 return S_OK; 3096 } 3097 return S_FALSE; 3098 } 3099 3100 /*********************************************************************** 3101 * ScriptShapeOpenType (USP10.@) 3102 * 3103 * Produce glyphs and visual attributes for a run. 3104 * 3105 * PARAMS 3106 * hdc [I] Device context. 3107 * psc [I/O] Opaque pointer to a script cache. 3108 * psa [I/O] Script analysis. 3109 * tagScript [I] The OpenType tag for the Script 3110 * tagLangSys [I] The OpenType tag for the Language 3111 * rcRangeChars[I] Array of Character counts in each range 3112 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures 3113 * cRanges [I] Count of ranges 3114 * pwcChars [I] Array of characters specifying the run. 3115 * cChars [I] Number of characters in pwcChars. 3116 * cMaxGlyphs [I] Length of pwOutGlyphs. 3117 * pwLogClust [O] Array of logical cluster info. 3118 * pCharProps [O] Array of character property values 3119 * pwOutGlyphs [O] Array of glyphs. 3120 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs 3121 * pcGlyphs [O] Number of glyphs returned. 3122 * 3123 * RETURNS 3124 * Success: S_OK 3125 * Failure: Non-zero HRESULT value. 3126 */ 3127 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc, 3128 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, 3129 OPENTYPE_TAG tagLangSys, int *rcRangeChars, 3130 TEXTRANGE_PROPERTIES **rpRangeProperties, 3131 int cRanges, const WCHAR *pwcChars, int cChars, 3132 int cMaxGlyphs, WORD *pwLogClust, 3133 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs, 3134 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs) 3135 { 3136 HRESULT hr; 3137 int i; 3138 unsigned int g; 3139 BOOL rtl; 3140 int cluster; 3141 static int once = 0; 3142 3143 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n", 3144 hdc, psc, psa, 3145 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4), 3146 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars), 3147 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs); 3148 3149 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL, 3150 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex); 3151 3152 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG; 3153 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY; 3154 3155 if (cRanges) 3156 if(!once++) FIXME("Ranges not supported yet\n"); 3157 3158 rtl = (psa && !psa->fLogicalOrder && psa->fRTL); 3159 3160 *pcGlyphs = cChars; 3161 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 3162 if (!pwLogClust) return E_FAIL; 3163 3164 ((ScriptCache *)*psc)->userScript = tagScript; 3165 ((ScriptCache *)*psc)->userLang = tagLangSys; 3166 3167 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */ 3168 for (i = 0; i < cChars; i++) 3169 { 3170 int idx = i; 3171 if (rtl) idx = cChars - 1 - i; 3172 /* FIXME: set to better values */ 3173 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER; 3174 pOutGlyphProps[i].sva.fClusterStart = 1; 3175 pOutGlyphProps[i].sva.fDiacritic = 0; 3176 pOutGlyphProps[i].sva.fZeroWidth = 0; 3177 pOutGlyphProps[i].sva.fReserved = 0; 3178 pOutGlyphProps[i].sva.fShapeReserved = 0; 3179 3180 /* FIXME: have the shaping engine set this */ 3181 pCharProps[i].fCanGlyphAlone = 0; 3182 3183 pwLogClust[i] = idx; 3184 } 3185 3186 if (psa && !psa->fNoGlyphIndex && ((ScriptCache *)*psc)->sfnt) 3187 { 3188 WCHAR *rChars; 3189 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr; 3190 3191 rChars = heap_alloc(sizeof(WCHAR) * cChars); 3192 if (!rChars) return E_OUTOFMEMORY; 3193 for (i = 0, g = 0, cluster = 0; i < cChars; i++) 3194 { 3195 int idx = i; 3196 DWORD chInput; 3197 3198 if (rtl) idx = cChars - 1 - i; 3199 if (!cluster) 3200 { 3201 chInput = decode_surrogate_pair(pwcChars, idx, cChars); 3202 if (!chInput) 3203 { 3204 if (psa->fRTL) 3205 chInput = mirror_char(pwcChars[idx]); 3206 else 3207 chInput = pwcChars[idx]; 3208 rChars[i] = chInput; 3209 } 3210 else 3211 { 3212 rChars[i] = pwcChars[idx]; 3213 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1]; 3214 cluster = 1; 3215 } 3216 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput))) 3217 { 3218 WORD glyph; 3219 if (!hdc) 3220 { 3221 heap_free(rChars); 3222 return E_PENDING; 3223 } 3224 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR) 3225 { 3226 heap_free(rChars); 3227 return S_FALSE; 3228 } 3229 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph); 3230 } 3231 g++; 3232 } 3233 else 3234 { 3235 int k; 3236 cluster--; 3237 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1]; 3238 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++) 3239 pwLogClust[k]--; 3240 } 3241 } 3242 *pcGlyphs = g; 3243 3244 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust); 3245 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust); 3246 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps); 3247 3248 for (i = 0; i < cChars; ++i) 3249 { 3250 /* Special case for tabs and joiners. As control characters, ZWNJ 3251 * and ZWJ would in principle get handled by the corresponding 3252 * shaping functions. However, since ZWNJ and ZWJ can get merged 3253 * into adjoining runs during itemisation, these don't generally 3254 * get classified as Script_Control. */ 3255 if (pwcChars[i] == 0x0009 || pwcChars[i] == ZWSP || pwcChars[i] == ZWNJ || pwcChars[i] == ZWJ) 3256 { 3257 pwOutGlyphs[pwLogClust[i]] = ((ScriptCache *)*psc)->sfp.wgBlank; 3258 pOutGlyphProps[pwLogClust[i]].sva.fZeroWidth = 1; 3259 } 3260 } 3261 heap_free(rChars); 3262 } 3263 else 3264 { 3265 TRACE("no glyph translation\n"); 3266 for (i = 0; i < cChars; i++) 3267 { 3268 int idx = i; 3269 /* No mirroring done here */ 3270 if (rtl) idx = cChars - 1 - i; 3271 pwOutGlyphs[i] = pwcChars[idx]; 3272 3273 if (!psa) 3274 continue; 3275 3276 /* overwrite some basic control glyphs to blank */ 3277 if (psa->fNoGlyphIndex) 3278 { 3279 if (pwcChars[idx] == ZWSP || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ) 3280 { 3281 pwOutGlyphs[i] = 0x20; 3282 pOutGlyphProps[i].sva.fZeroWidth = 1; 3283 } 3284 } 3285 else if (psa->eScript == Script_Control || pwcChars[idx] == ZWSP 3286 || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ) 3287 { 3288 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A || 3289 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C) 3290 { 3291 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank; 3292 pOutGlyphProps[i].sva.fZeroWidth = 1; 3293 } 3294 } 3295 } 3296 } 3297 3298 return S_OK; 3299 } 3300 3301 3302 /*********************************************************************** 3303 * ScriptShape (USP10.@) 3304 * 3305 * Produce glyphs and visual attributes for a run. 3306 * 3307 * PARAMS 3308 * hdc [I] Device context. 3309 * psc [I/O] Opaque pointer to a script cache. 3310 * pwcChars [I] Array of characters specifying the run. 3311 * cChars [I] Number of characters in pwcChars. 3312 * cMaxGlyphs [I] Length of pwOutGlyphs. 3313 * psa [I/O] Script analysis. 3314 * pwOutGlyphs [O] Array of glyphs. 3315 * pwLogClust [O] Array of logical cluster info. 3316 * psva [O] Array of visual attributes. 3317 * pcGlyphs [O] Number of glyphs returned. 3318 * 3319 * RETURNS 3320 * Success: S_OK 3321 * Failure: Non-zero HRESULT value. 3322 */ 3323 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars, 3324 int cChars, int cMaxGlyphs, 3325 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust, 3326 SCRIPT_VISATTR *psva, int *pcGlyphs) 3327 { 3328 HRESULT hr; 3329 int i; 3330 SCRIPT_CHARPROP *charProps; 3331 SCRIPT_GLYPHPROP *glyphProps; 3332 3333 if (!psva || !pcGlyphs) return E_INVALIDARG; 3334 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY; 3335 3336 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars); 3337 if (!charProps) return E_OUTOFMEMORY; 3338 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs); 3339 if (!glyphProps) 3340 { 3341 heap_free(charProps); 3342 return E_OUTOFMEMORY; 3343 } 3344 3345 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs); 3346 3347 if (SUCCEEDED(hr)) 3348 { 3349 for (i = 0; i < *pcGlyphs; i++) 3350 psva[i] = glyphProps[i].sva; 3351 } 3352 3353 heap_free(charProps); 3354 heap_free(glyphProps); 3355 3356 return hr; 3357 } 3358 3359 /*********************************************************************** 3360 * ScriptPlaceOpenType (USP10.@) 3361 * 3362 * Produce advance widths for a run. 3363 * 3364 * PARAMS 3365 * hdc [I] Device context. 3366 * psc [I/O] Opaque pointer to a script cache. 3367 * psa [I/O] Script analysis. 3368 * tagScript [I] The OpenType tag for the Script 3369 * tagLangSys [I] The OpenType tag for the Language 3370 * rcRangeChars[I] Array of Character counts in each range 3371 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures 3372 * cRanges [I] Count of ranges 3373 * pwcChars [I] Array of characters specifying the run. 3374 * pwLogClust [I] Array of logical cluster info 3375 * pCharProps [I] Array of character property values 3376 * cChars [I] Number of characters in pwcChars. 3377 * pwGlyphs [I] Array of glyphs. 3378 * pGlyphProps [I] Array of attributes for the retrieved glyphs 3379 * cGlyphs [I] Count of Glyphs 3380 * piAdvance [O] Array of advance widths. 3381 * pGoffset [O] Glyph offsets. 3382 * pABC [O] Combined ABC width. 3383 * 3384 * RETURNS 3385 * Success: S_OK 3386 * Failure: Non-zero HRESULT value. 3387 */ 3388 3389 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, 3390 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, 3391 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties, 3392 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust, 3393 SCRIPT_CHARPROP *pCharProps, int cChars, 3394 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps, 3395 int cGlyphs, int *piAdvance, 3396 GOFFSET *pGoffset, ABC *pABC 3397 ) 3398 { 3399 HRESULT hr; 3400 int i; 3401 static int once = 0; 3402 3403 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n", 3404 hdc, psc, psa, 3405 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4), 3406 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars), 3407 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance, 3408 pGoffset, pABC); 3409 3410 if (!pGlyphProps) return E_INVALIDARG; 3411 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 3412 if (!pGoffset) return E_FAIL; 3413 3414 if (cRanges) 3415 if (!once++) FIXME("Ranges not supported yet\n"); 3416 3417 ((ScriptCache *)*psc)->userScript = tagScript; 3418 ((ScriptCache *)*psc)->userLang = tagLangSys; 3419 3420 if (pABC) memset(pABC, 0, sizeof(ABC)); 3421 for (i = 0; i < cGlyphs; i++) 3422 { 3423 ABC abc; 3424 if (pGlyphProps[i].sva.fZeroWidth) 3425 { 3426 abc.abcA = abc.abcB = abc.abcC = 0; 3427 } 3428 else if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc)) 3429 { 3430 BOOL ret; 3431 if (!hdc) return E_PENDING; 3432 if (get_cache_pitch_family(psc) & TMPF_TRUETYPE) 3433 { 3434 if (psa->fNoGlyphIndex) 3435 ret = GetCharABCWidthsW(hdc, pwGlyphs[i], pwGlyphs[i], &abc); 3436 else 3437 ret = GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc); 3438 if (!ret) return S_FALSE; 3439 } 3440 else 3441 { 3442 INT width; 3443 if (psa->fNoGlyphIndex) 3444 ret = GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width); 3445 else 3446 ret = GetCharWidthI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &width); 3447 if (!ret) return S_FALSE; 3448 abc.abcB = width; 3449 abc.abcA = abc.abcC = 0; 3450 } 3451 set_cache_glyph_widths(psc, pwGlyphs[i], &abc); 3452 } 3453 if (pABC) 3454 { 3455 pABC->abcA += abc.abcA; 3456 pABC->abcB += abc.abcB; 3457 pABC->abcC += abc.abcC; 3458 } 3459 /* FIXME: set to more reasonable values */ 3460 pGoffset[i].du = pGoffset[i].dv = 0; 3461 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC; 3462 } 3463 3464 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset); 3465 3466 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC); 3467 return S_OK; 3468 } 3469 3470 /*********************************************************************** 3471 * ScriptPlace (USP10.@) 3472 * 3473 * Produce advance widths for a run. 3474 * 3475 * PARAMS 3476 * hdc [I] Device context. 3477 * psc [I/O] Opaque pointer to a script cache. 3478 * pwGlyphs [I] Array of glyphs. 3479 * cGlyphs [I] Number of glyphs in pwGlyphs. 3480 * psva [I] Array of visual attributes. 3481 * psa [I/O] String analysis. 3482 * piAdvance [O] Array of advance widths. 3483 * pGoffset [O] Glyph offsets. 3484 * pABC [O] Combined ABC width. 3485 * 3486 * RETURNS 3487 * Success: S_OK 3488 * Failure: Non-zero HRESULT value. 3489 */ 3490 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs, 3491 int cGlyphs, const SCRIPT_VISATTR *psva, 3492 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC ) 3493 { 3494 HRESULT hr; 3495 SCRIPT_GLYPHPROP *glyphProps; 3496 int i; 3497 3498 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa, 3499 piAdvance, pGoffset, pABC); 3500 3501 if (!psva) return E_INVALIDARG; 3502 if (!pGoffset) return E_FAIL; 3503 3504 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs); 3505 if (!glyphProps) return E_OUTOFMEMORY; 3506 3507 for (i = 0; i < cGlyphs; i++) 3508 glyphProps[i].sva = psva[i]; 3509 3510 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC); 3511 3512 heap_free(glyphProps); 3513 3514 return hr; 3515 } 3516 3517 /*********************************************************************** 3518 * ScriptGetCMap (USP10.@) 3519 * 3520 * Retrieve glyph indices. 3521 * 3522 * PARAMS 3523 * hdc [I] Device context. 3524 * psc [I/O] Opaque pointer to a script cache. 3525 * pwcInChars [I] Array of Unicode characters. 3526 * cChars [I] Number of characters in pwcInChars. 3527 * dwFlags [I] Flags. 3528 * pwOutGlyphs [O] Buffer to receive the array of glyph indices. 3529 * 3530 * RETURNS 3531 * Success: S_OK 3532 * Failure: Non-zero HRESULT value. 3533 */ 3534 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars, 3535 int cChars, DWORD dwFlags, WORD *pwOutGlyphs) 3536 { 3537 HRESULT hr; 3538 int i; 3539 3540 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars), 3541 cChars, dwFlags, pwOutGlyphs); 3542 3543 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 3544 3545 hr = S_OK; 3546 3547 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE)) 3548 { 3549 for (i = 0; i < cChars; i++) 3550 { 3551 WCHAR inChar; 3552 if (dwFlags == SGCM_RTL) 3553 inChar = mirror_char(pwcInChars[i]); 3554 else 3555 inChar = pwcInChars[i]; 3556 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar))) 3557 { 3558 WORD glyph; 3559 if (!hdc) return E_PENDING; 3560 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE; 3561 if (glyph == 0xffff) 3562 { 3563 hr = S_FALSE; 3564 glyph = 0x0; 3565 } 3566 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph); 3567 } 3568 } 3569 } 3570 else 3571 { 3572 TRACE("no glyph translation\n"); 3573 for (i = 0; i < cChars; i++) 3574 { 3575 WCHAR inChar; 3576 if (dwFlags == SGCM_RTL) 3577 inChar = mirror_char(pwcInChars[i]); 3578 else 3579 inChar = pwcInChars[i]; 3580 pwOutGlyphs[i] = inChar; 3581 } 3582 } 3583 return hr; 3584 } 3585 3586 /*********************************************************************** 3587 * ScriptTextOut (USP10.@) 3588 * 3589 */ 3590 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions, 3591 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved, 3592 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance, 3593 const int *piJustify, const GOFFSET *pGoffset) 3594 { 3595 HRESULT hr = S_OK; 3596 INT i, dir = 1; 3597 INT *lpDx; 3598 WORD *reordered_glyphs = (WORD *)pwGlyphs; 3599 3600 TRACE("(%p, %p, %d, %d, %08x, %s, %p, %p, %d, %p, %d, %p, %p, %p)\n", 3601 hdc, psc, x, y, fuOptions, wine_dbgstr_rect(lprc), psa, pwcReserved, iReserved, pwGlyphs, cGlyphs, 3602 piAdvance, piJustify, pGoffset); 3603 3604 if (!hdc || !psc) return E_INVALIDARG; 3605 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG; 3606 3607 fuOptions &= ETO_CLIPPED + ETO_OPAQUE; 3608 fuOptions |= ETO_IGNORELANGUAGE; 3609 if (!psa->fNoGlyphIndex) /* Have Glyphs? */ 3610 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */ 3611 3612 if (!(lpDx = heap_calloc(cGlyphs, 2 * sizeof(*lpDx)))) 3613 return E_OUTOFMEMORY; 3614 fuOptions |= ETO_PDY; 3615 3616 if (psa->fRTL && psa->fLogicalOrder) 3617 { 3618 if (!(reordered_glyphs = heap_calloc(cGlyphs, sizeof(*reordered_glyphs)))) 3619 { 3620 heap_free( lpDx ); 3621 return E_OUTOFMEMORY; 3622 } 3623 3624 for (i = 0; i < cGlyphs; i++) 3625 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i]; 3626 dir = -1; 3627 } 3628 3629 for (i = 0; i < cGlyphs; i++) 3630 { 3631 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i; 3632 lpDx[i * 2] = piAdvance[orig_index]; 3633 lpDx[i * 2 + 1] = 0; 3634 3635 if (pGoffset) 3636 { 3637 if (i == 0) 3638 { 3639 x += pGoffset[orig_index].du * dir; 3640 y += pGoffset[orig_index].dv; 3641 } 3642 else 3643 { 3644 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir; 3645 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv; 3646 } 3647 lpDx[i * 2] -= pGoffset[orig_index].du * dir; 3648 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv; 3649 } 3650 } 3651 3652 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx)) 3653 hr = S_FALSE; 3654 3655 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs ); 3656 heap_free(lpDx); 3657 3658 return hr; 3659 } 3660 3661 /*********************************************************************** 3662 * ScriptCacheGetHeight (USP10.@) 3663 * 3664 * Retrieve the height of the font in the cache. 3665 * 3666 * PARAMS 3667 * hdc [I] Device context. 3668 * psc [I/O] Opaque pointer to a script cache. 3669 * height [O] Receives font height. 3670 * 3671 * RETURNS 3672 * Success: S_OK 3673 * Failure: Non-zero HRESULT value. 3674 */ 3675 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height) 3676 { 3677 HRESULT hr; 3678 3679 TRACE("(%p, %p, %p)\n", hdc, psc, height); 3680 3681 if (!height) return E_INVALIDARG; 3682 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 3683 3684 *height = get_cache_height(psc); 3685 return S_OK; 3686 } 3687 3688 /*********************************************************************** 3689 * ScriptGetGlyphABCWidth (USP10.@) 3690 * 3691 * Retrieve the width of a glyph. 3692 * 3693 * PARAMS 3694 * hdc [I] Device context. 3695 * psc [I/O] Opaque pointer to a script cache. 3696 * glyph [I] Glyph to retrieve the width for. 3697 * abc [O] ABC widths of the glyph. 3698 * 3699 * RETURNS 3700 * Success: S_OK 3701 * Failure: Non-zero HRESULT value. 3702 */ 3703 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc) 3704 { 3705 HRESULT hr; 3706 3707 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc); 3708 3709 if (!abc) return E_INVALIDARG; 3710 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 3711 3712 if (!get_cache_glyph_widths(psc, glyph, abc)) 3713 { 3714 if (!hdc) return E_PENDING; 3715 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE)) 3716 { 3717 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE; 3718 } 3719 else 3720 { 3721 INT width; 3722 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE; 3723 abc->abcB = width; 3724 abc->abcA = abc->abcC = 0; 3725 } 3726 set_cache_glyph_widths(psc, glyph, abc); 3727 } 3728 return S_OK; 3729 } 3730 3731 /*********************************************************************** 3732 * ScriptLayout (USP10.@) 3733 * 3734 * Map embedding levels to visual and/or logical order. 3735 * 3736 * PARAMS 3737 * runs [I] Size of level array. 3738 * level [I] Array of embedding levels. 3739 * vistolog [O] Map of embedding levels from visual to logical order. 3740 * logtovis [O] Map of embedding levels from logical to visual order. 3741 * 3742 * RETURNS 3743 * Success: S_OK 3744 * Failure: Non-zero HRESULT value. 3745 * 3746 */ 3747 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis) 3748 { 3749 int* indexs; 3750 int ich; 3751 3752 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis); 3753 3754 if (!level || (!vistolog && !logtovis)) 3755 return E_INVALIDARG; 3756 3757 if (!(indexs = heap_calloc(runs, sizeof(*indexs)))) 3758 return E_OUTOFMEMORY; 3759 3760 if (vistolog) 3761 { 3762 for( ich = 0; ich < runs; ich++) 3763 indexs[ich] = ich; 3764 3765 ich = 0; 3766 while (ich < runs) 3767 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE); 3768 memcpy(vistolog, indexs, runs * sizeof(*vistolog)); 3769 } 3770 3771 if (logtovis) 3772 { 3773 for( ich = 0; ich < runs; ich++) 3774 indexs[ich] = ich; 3775 3776 ich = 0; 3777 while (ich < runs) 3778 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE); 3779 memcpy(logtovis, indexs, runs * sizeof(*logtovis)); 3780 } 3781 heap_free(indexs); 3782 3783 return S_OK; 3784 } 3785 3786 /*********************************************************************** 3787 * ScriptStringGetLogicalWidths (USP10.@) 3788 * 3789 * Returns logical widths from a string analysis. 3790 * 3791 * PARAMS 3792 * ssa [I] string analysis. 3793 * piDx [O] logical widths returned. 3794 * 3795 * RETURNS 3796 * Success: S_OK 3797 * Failure: a non-zero HRESULT. 3798 */ 3799 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx) 3800 { 3801 int i, j, next = 0; 3802 StringAnalysis *analysis = ssa; 3803 3804 TRACE("%p, %p\n", ssa, piDx); 3805 3806 if (!analysis) return S_FALSE; 3807 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE; 3808 3809 for (i = 0; i < analysis->numItems; i++) 3810 { 3811 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos; 3812 int direction = 1; 3813 3814 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder) 3815 direction = -1; 3816 3817 for (j = 0; j < cChar; j++) 3818 { 3819 int k; 3820 int glyph = analysis->glyphs[i].pwLogClust[j]; 3821 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust, 3822 cChar, j, direction, NULL, NULL); 3823 int advance = get_glyph_cluster_advance(analysis->glyphs[i].piAdvance, analysis->glyphs[i].psva, analysis->glyphs[i].pwLogClust, analysis->glyphs[i].numGlyphs, cChar, glyph, direction); 3824 3825 for (k = 0; k < clust_size; k++) 3826 { 3827 piDx[next] = advance / clust_size; 3828 next++; 3829 if (k) j++; 3830 } 3831 } 3832 } 3833 return S_OK; 3834 } 3835 3836 /*********************************************************************** 3837 * ScriptStringValidate (USP10.@) 3838 * 3839 * Validate a string analysis. 3840 * 3841 * PARAMS 3842 * ssa [I] string analysis. 3843 * 3844 * RETURNS 3845 * Success: S_OK 3846 * Failure: S_FALSE if invalid sequences are found 3847 * or a non-zero HRESULT if it fails. 3848 */ 3849 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa) 3850 { 3851 StringAnalysis *analysis = ssa; 3852 3853 TRACE("(%p)\n", ssa); 3854 3855 if (!analysis) return E_INVALIDARG; 3856 return analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_INVALID ? S_FALSE : S_OK; 3857 } 3858 3859 /*********************************************************************** 3860 * ScriptString_pSize (USP10.@) 3861 * 3862 * Retrieve width and height of an analysed string. 3863 * 3864 * PARAMS 3865 * ssa [I] string analysis. 3866 * 3867 * RETURNS 3868 * Success: Pointer to a SIZE structure. 3869 * Failure: NULL 3870 */ 3871 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa) 3872 { 3873 int i, j; 3874 StringAnalysis *analysis = ssa; 3875 3876 TRACE("(%p)\n", ssa); 3877 3878 if (!analysis) return NULL; 3879 if (!(analysis->ssa_flags & SSA_GLYPHS)) return NULL; 3880 3881 if (!(analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_SIZE)) 3882 { 3883 analysis->sz.cy = analysis->glyphs[0].sc->tm.tmHeight; 3884 3885 analysis->sz.cx = 0; 3886 for (i = 0; i < analysis->numItems; i++) 3887 { 3888 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz.cy) 3889 analysis->sz.cy = analysis->glyphs[i].sc->tm.tmHeight; 3890 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++) 3891 analysis->sz.cx += analysis->glyphs[i].piAdvance[j]; 3892 } 3893 } 3894 return &analysis->sz; 3895 } 3896 3897 /*********************************************************************** 3898 * ScriptString_pLogAttr (USP10.@) 3899 * 3900 * Retrieve logical attributes of an analysed string. 3901 * 3902 * PARAMS 3903 * ssa [I] string analysis. 3904 * 3905 * RETURNS 3906 * Success: Pointer to an array of SCRIPT_LOGATTR structures. 3907 * Failure: NULL 3908 */ 3909 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa) 3910 { 3911 StringAnalysis *analysis = ssa; 3912 3913 TRACE("(%p)\n", ssa); 3914 3915 if (!analysis) return NULL; 3916 if (!(analysis->ssa_flags & SSA_BREAK)) return NULL; 3917 return analysis->logattrs; 3918 } 3919 3920 /*********************************************************************** 3921 * ScriptString_pcOutChars (USP10.@) 3922 * 3923 * Retrieve the length of a string after clipping. 3924 * 3925 * PARAMS 3926 * ssa [I] String analysis. 3927 * 3928 * RETURNS 3929 * Success: Pointer to the length. 3930 * Failure: NULL 3931 */ 3932 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa) 3933 { 3934 StringAnalysis *analysis = ssa; 3935 3936 TRACE("(%p)\n", ssa); 3937 3938 if (!analysis) return NULL; 3939 return &analysis->clip_len; 3940 } 3941 3942 /*********************************************************************** 3943 * ScriptStringGetOrder (USP10.@) 3944 * 3945 * Retrieve a glyph order map. 3946 * 3947 * PARAMS 3948 * ssa [I] String analysis. 3949 * order [I/O] Array of glyph positions. 3950 * 3951 * RETURNS 3952 * Success: S_OK 3953 * Failure: a non-zero HRESULT. 3954 */ 3955 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order) 3956 { 3957 int i, j; 3958 unsigned int k; 3959 StringAnalysis *analysis = ssa; 3960 3961 TRACE("(%p)\n", ssa); 3962 3963 if (!analysis) return S_FALSE; 3964 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE; 3965 3966 /* FIXME: handle RTL scripts */ 3967 for (i = 0, k = 0; i < analysis->numItems; i++) 3968 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++) 3969 order[k] = k; 3970 3971 return S_OK; 3972 } 3973 3974 /*********************************************************************** 3975 * ScriptGetLogicalWidths (USP10.@) 3976 * 3977 * Convert advance widths to logical widths. 3978 * 3979 * PARAMS 3980 * sa [I] Script analysis. 3981 * nbchars [I] Number of characters. 3982 * nbglyphs [I] Number of glyphs. 3983 * glyph_width [I] Array of glyph widths. 3984 * log_clust [I] Array of logical clusters. 3985 * sva [I] Visual attributes. 3986 * widths [O] Array of logical widths. 3987 * 3988 * RETURNS 3989 * Success: S_OK 3990 * Failure: a non-zero HRESULT. 3991 */ 3992 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs, 3993 const int *advances, const WORD *log_clust, 3994 const SCRIPT_VISATTR *sva, int *widths) 3995 { 3996 int i, next = 0, direction; 3997 3998 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n", 3999 sa, nbchars, nbglyphs, advances, log_clust, sva, widths); 4000 4001 if (sa->fRTL && !sa->fLogicalOrder) 4002 direction = -1; 4003 else 4004 direction = 1; 4005 4006 for (i = 0; i < nbchars; i++) 4007 { 4008 int clust_size = get_cluster_size(log_clust, nbchars, i, direction, NULL, NULL); 4009 int advance = get_glyph_cluster_advance(advances, sva, log_clust, nbglyphs, nbchars, log_clust[i], direction); 4010 int j; 4011 4012 for (j = 0; j < clust_size; j++) 4013 { 4014 widths[next] = advance / clust_size; 4015 next++; 4016 if (j) i++; 4017 } 4018 } 4019 4020 return S_OK; 4021 } 4022 4023 /*********************************************************************** 4024 * ScriptApplyLogicalWidth (USP10.@) 4025 * 4026 * Generate glyph advance widths. 4027 * 4028 * PARAMS 4029 * dx [I] Array of logical advance widths. 4030 * num_chars [I] Number of characters. 4031 * num_glyphs [I] Number of glyphs. 4032 * log_clust [I] Array of logical clusters. 4033 * sva [I] Visual attributes. 4034 * advance [I] Array of glyph advance widths. 4035 * sa [I] Script analysis. 4036 * abc [I/O] Summed ABC widths. 4037 * justify [O] Array of glyph advance widths. 4038 * 4039 * RETURNS 4040 * Success: S_OK 4041 * Failure: a non-zero HRESULT. 4042 */ 4043 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs, 4044 const WORD *log_clust, const SCRIPT_VISATTR *sva, 4045 const int *advance, const SCRIPT_ANALYSIS *sa, 4046 ABC *abc, int *justify) 4047 { 4048 int i; 4049 4050 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n", 4051 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify); 4052 4053 for (i = 0; i < num_chars; i++) justify[i] = advance[i]; 4054 return S_OK; 4055 } 4056 4057 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance, 4058 int num_glyphs, int dx, int min_kashida, int *justify) 4059 { 4060 int i; 4061 4062 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify); 4063 4064 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i]; 4065 return S_OK; 4066 } 4067 4068 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags) 4069 { 4070 HRESULT hr; 4071 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG; 4072 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 4073 4074 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags); 4075 } 4076 4077 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags) 4078 { 4079 HRESULT hr; 4080 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG; 4081 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 4082 4083 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags); 4084 } 4085 4086 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags) 4087 { 4088 HRESULT hr; 4089 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG; 4090 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr; 4091 4092 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags); 4093 } 4094 4095 #ifdef __REACTOS__ 4096 BOOL gbLpkPresent = FALSE; 4097 VOID WINAPI LpkPresent() 4098 { 4099 gbLpkPresent = TRUE; /* Turn it on this way! Wine is out of control! */ 4100 } 4101 #endif 4102