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