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