| 1 | /* | 
| 2 |  * Copyright © 2011,2012,2013  Google, Inc. | 
| 3 |  * | 
| 4 |  *  This is part of HarfBuzz, a text shaping library. | 
| 5 |  * | 
| 6 |  * Permission is hereby granted, without written agreement and without | 
| 7 |  * license or royalty fees, to use, copy, modify, and distribute this | 
| 8 |  * software and its documentation for any purpose, provided that the | 
| 9 |  * above copyright notice and the following two paragraphs appear in | 
| 10 |  * all copies of this software. | 
| 11 |  * | 
| 12 |  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 
| 13 |  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 
| 14 |  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 
| 15 |  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | 
| 16 |  * DAMAGE. | 
| 17 |  * | 
| 18 |  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 
| 19 |  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 
| 20 |  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS | 
| 21 |  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | 
| 22 |  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 
| 23 |  * | 
| 24 |  * Google Author(s): Behdad Esfahbod | 
| 25 |  */ | 
| 26 |  | 
| 27 | #include "hb.hh" | 
| 28 |  | 
| 29 | #ifdef HAVE_UNISCRIBE | 
| 30 |  | 
| 31 | #ifdef HB_NO_OT_TAG | 
| 32 | #error "Cannot compile 'uniscribe' shaper with HB_NO_OT_TAG." | 
| 33 | #endif | 
| 34 |  | 
| 35 | #include "hb-shaper-impl.hh" | 
| 36 |  | 
| 37 | #include <windows.h> | 
| 38 | #include <usp10.h> | 
| 39 | #include <rpc.h> | 
| 40 |  | 
| 41 | #ifndef E_NOT_SUFFICIENT_BUFFER | 
| 42 | #define E_NOT_SUFFICIENT_BUFFER HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER) | 
| 43 | #endif | 
| 44 |  | 
| 45 | #include "hb-uniscribe.h" | 
| 46 |  | 
| 47 | #include "hb-open-file.hh" | 
| 48 | #include "hb-ot-name-table.hh" | 
| 49 | #include "hb-ot-layout.h" | 
| 50 |  | 
| 51 |  | 
| 52 | /** | 
| 53 |  * SECTION:hb-uniscribe | 
| 54 |  * @title: hb-uniscribe | 
| 55 |  * @short_description: Windows integration | 
| 56 |  * @include: hb-uniscribe.h | 
| 57 |  * | 
| 58 |  * Functions for using HarfBuzz with Windows fonts. | 
| 59 |  **/ | 
| 60 |  | 
| 61 | typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/( | 
| 62 |   const WCHAR *pwcInChars, | 
| 63 |   int cInChars, | 
| 64 |   int cMaxItems, | 
| 65 |   const SCRIPT_CONTROL *psControl, | 
| 66 |   const SCRIPT_STATE *psState, | 
| 67 |   SCRIPT_ITEM *pItems, | 
| 68 |   OPENTYPE_TAG *pScriptTags, | 
| 69 |   int *pcItems | 
| 70 | ); | 
| 71 |  | 
| 72 | typedef HRESULT (WINAPI *SSOT) /*ScriptShapeOpenType*/( | 
| 73 |   HDC hdc, | 
| 74 |   SCRIPT_CACHE *psc, | 
| 75 |   SCRIPT_ANALYSIS *psa, | 
| 76 |   OPENTYPE_TAG tagScript, | 
| 77 |   OPENTYPE_TAG tagLangSys, | 
| 78 |   int *rcRangeChars, | 
| 79 |   TEXTRANGE_PROPERTIES **rpRangeProperties, | 
| 80 |   int cRanges, | 
| 81 |   const WCHAR *pwcChars, | 
| 82 |   int cChars, | 
| 83 |   int cMaxGlyphs, | 
| 84 |   WORD *pwLogClust, | 
| 85 |   SCRIPT_CHARPROP *pCharProps, | 
| 86 |   WORD *pwOutGlyphs, | 
| 87 |   SCRIPT_GLYPHPROP *pOutGlyphProps, | 
| 88 |   int *pcGlyphs | 
| 89 | ); | 
| 90 |  | 
| 91 | typedef HRESULT (WINAPI *SPOT) /*ScriptPlaceOpenType*/( | 
| 92 |   HDC hdc, | 
| 93 |   SCRIPT_CACHE *psc, | 
| 94 |   SCRIPT_ANALYSIS *psa, | 
| 95 |   OPENTYPE_TAG tagScript, | 
| 96 |   OPENTYPE_TAG tagLangSys, | 
| 97 |   int *rcRangeChars, | 
| 98 |   TEXTRANGE_PROPERTIES **rpRangeProperties, | 
| 99 |   int cRanges, | 
| 100 |   const WCHAR *pwcChars, | 
| 101 |   WORD *pwLogClust, | 
| 102 |   SCRIPT_CHARPROP *pCharProps, | 
| 103 |   int cChars, | 
| 104 |   const WORD *pwGlyphs, | 
| 105 |   const SCRIPT_GLYPHPROP *pGlyphProps, | 
| 106 |   int cGlyphs, | 
| 107 |   int *piAdvance, | 
| 108 |   GOFFSET *pGoffset, | 
| 109 |   ABC *pABC | 
| 110 | ); | 
| 111 |  | 
| 112 |  | 
| 113 | /* Fallback implementations. */ | 
| 114 |  | 
| 115 | static HRESULT WINAPI | 
| 116 | hb_ScriptItemizeOpenType( | 
| 117 |   const WCHAR *pwcInChars, | 
| 118 |   int cInChars, | 
| 119 |   int cMaxItems, | 
| 120 |   const SCRIPT_CONTROL *psControl, | 
| 121 |   const SCRIPT_STATE *psState, | 
| 122 |   SCRIPT_ITEM *pItems, | 
| 123 |   OPENTYPE_TAG *pScriptTags, | 
| 124 |   int *pcItems | 
| 125 | ) | 
| 126 | { | 
| 127 | { | 
| 128 |   return ScriptItemize (pwcInChars, | 
| 129 | 			cInChars, | 
| 130 | 			cMaxItems, | 
| 131 | 			psControl, | 
| 132 | 			psState, | 
| 133 | 			pItems, | 
| 134 | 			pcItems); | 
| 135 | } | 
| 136 | } | 
| 137 |  | 
| 138 | static HRESULT WINAPI | 
| 139 | hb_ScriptShapeOpenType( | 
| 140 |   HDC hdc, | 
| 141 |   SCRIPT_CACHE *psc, | 
| 142 |   SCRIPT_ANALYSIS *psa, | 
| 143 |   OPENTYPE_TAG tagScript, | 
| 144 |   OPENTYPE_TAG tagLangSys, | 
| 145 |   int *rcRangeChars, | 
| 146 |   TEXTRANGE_PROPERTIES **rpRangeProperties, | 
| 147 |   int cRanges, | 
| 148 |   const WCHAR *pwcChars, | 
| 149 |   int cChars, | 
| 150 |   int cMaxGlyphs, | 
| 151 |   WORD *pwLogClust, | 
| 152 |   SCRIPT_CHARPROP *pCharProps, | 
| 153 |   WORD *pwOutGlyphs, | 
| 154 |   SCRIPT_GLYPHPROP *pOutGlyphProps, | 
| 155 |   int *pcGlyphs | 
| 156 | ) | 
| 157 | { | 
| 158 |   SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pOutGlyphProps; | 
| 159 |   return ScriptShape (hdc, | 
| 160 | 		      psc, | 
| 161 | 		      pwcChars, | 
| 162 | 		      cChars, | 
| 163 | 		      cMaxGlyphs, | 
| 164 | 		      psa, | 
| 165 | 		      pwOutGlyphs, | 
| 166 | 		      pwLogClust, | 
| 167 | 		      psva, | 
| 168 | 		      pcGlyphs); | 
| 169 | } | 
| 170 |  | 
| 171 | static HRESULT WINAPI | 
| 172 | hb_ScriptPlaceOpenType( | 
| 173 |   HDC hdc, | 
| 174 |   SCRIPT_CACHE *psc, | 
| 175 |   SCRIPT_ANALYSIS *psa, | 
| 176 |   OPENTYPE_TAG tagScript, | 
| 177 |   OPENTYPE_TAG tagLangSys, | 
| 178 |   int *rcRangeChars, | 
| 179 |   TEXTRANGE_PROPERTIES **rpRangeProperties, | 
| 180 |   int cRanges, | 
| 181 |   const WCHAR *pwcChars, | 
| 182 |   WORD *pwLogClust, | 
| 183 |   SCRIPT_CHARPROP *pCharProps, | 
| 184 |   int cChars, | 
| 185 |   const WORD *pwGlyphs, | 
| 186 |   const SCRIPT_GLYPHPROP *pGlyphProps, | 
| 187 |   int cGlyphs, | 
| 188 |   int *piAdvance, | 
| 189 |   GOFFSET *pGoffset, | 
| 190 |   ABC *pABC | 
| 191 | ) | 
| 192 | { | 
| 193 |   SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pGlyphProps; | 
| 194 |   return ScriptPlace (hdc, | 
| 195 | 		      psc, | 
| 196 | 		      pwGlyphs, | 
| 197 | 		      cGlyphs, | 
| 198 | 		      psva, | 
| 199 | 		      psa, | 
| 200 | 		      piAdvance, | 
| 201 | 		      pGoffset, | 
| 202 | 		      pABC); | 
| 203 | } | 
| 204 |  | 
| 205 |  | 
| 206 | struct hb_uniscribe_shaper_funcs_t | 
| 207 | { | 
| 208 |   SIOT ScriptItemizeOpenType; | 
| 209 |   SSOT ScriptShapeOpenType; | 
| 210 |   SPOT ScriptPlaceOpenType; | 
| 211 |  | 
| 212 |   void init () | 
| 213 |   { | 
| 214 |     HMODULE hinstLib; | 
| 215 |     this->ScriptItemizeOpenType = nullptr; | 
| 216 |     this->ScriptShapeOpenType   = nullptr; | 
| 217 |     this->ScriptPlaceOpenType   = nullptr; | 
| 218 |  | 
| 219 |     hinstLib = GetModuleHandle (TEXT ("usp10.dll" )); | 
| 220 |     if (hinstLib) | 
| 221 |     { | 
| 222 | #pragma GCC diagnostic push | 
| 223 | #pragma GCC diagnostic ignored "-Wcast-function-type" | 
| 224 |       this->ScriptItemizeOpenType = (SIOT) GetProcAddress (hinstLib, "ScriptItemizeOpenType" ); | 
| 225 |       this->ScriptShapeOpenType   = (SSOT) GetProcAddress (hinstLib, "ScriptShapeOpenType" ); | 
| 226 |       this->ScriptPlaceOpenType   = (SPOT) GetProcAddress (hinstLib, "ScriptPlaceOpenType" ); | 
| 227 | #pragma GCC diagnostic pop | 
| 228 |     } | 
| 229 |     if (!this->ScriptItemizeOpenType || | 
| 230 | 	!this->ScriptShapeOpenType   || | 
| 231 | 	!this->ScriptPlaceOpenType) | 
| 232 |     { | 
| 233 |       DEBUG_MSG (UNISCRIBE, nullptr, "OpenType versions of functions not found; falling back." ); | 
| 234 |       this->ScriptItemizeOpenType = hb_ScriptItemizeOpenType; | 
| 235 |       this->ScriptShapeOpenType   = hb_ScriptShapeOpenType; | 
| 236 |       this->ScriptPlaceOpenType   = hb_ScriptPlaceOpenType; | 
| 237 |     } | 
| 238 |   } | 
| 239 | }; | 
| 240 |  | 
| 241 | #if HB_USE_ATEXIT | 
| 242 | static void free_static_uniscribe_shaper_funcs (); | 
| 243 | #endif | 
| 244 |  | 
| 245 | static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_uniscribe_shaper_funcs_t, | 
| 246 | 									 hb_uniscribe_shaper_funcs_lazy_loader_t> | 
| 247 | { | 
| 248 |   static hb_uniscribe_shaper_funcs_t *create () | 
| 249 |   { | 
| 250 |     hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t)); | 
| 251 |     if (unlikely (!funcs)) | 
| 252 |       return nullptr; | 
| 253 |  | 
| 254 |     funcs->init (); | 
| 255 |  | 
| 256 | #if HB_USE_ATEXIT | 
| 257 |     atexit (free_static_uniscribe_shaper_funcs); | 
| 258 | #endif | 
| 259 |  | 
| 260 |     return funcs; | 
| 261 |   } | 
| 262 |   static void destroy (hb_uniscribe_shaper_funcs_t *p) | 
| 263 |   { | 
| 264 |     free ((void *) p); | 
| 265 |   } | 
| 266 |   static hb_uniscribe_shaper_funcs_t *get_null () | 
| 267 |   { | 
| 268 |     return nullptr; | 
| 269 |   } | 
| 270 | } static_uniscribe_shaper_funcs; | 
| 271 |  | 
| 272 | #if HB_USE_ATEXIT | 
| 273 | static | 
| 274 | void free_static_uniscribe_shaper_funcs () | 
| 275 | { | 
| 276 |   static_uniscribe_shaper_funcs.free_instance (); | 
| 277 | } | 
| 278 | #endif | 
| 279 |  | 
| 280 | static hb_uniscribe_shaper_funcs_t * | 
| 281 | hb_uniscribe_shaper_get_funcs () | 
| 282 | { | 
| 283 |   return static_uniscribe_shaper_funcs.get_unconst (); | 
| 284 | } | 
| 285 |  | 
| 286 |  | 
| 287 | struct active_feature_t { | 
| 288 |   OPENTYPE_FEATURE_RECORD rec; | 
| 289 |   unsigned int order; | 
| 290 |  | 
| 291 |   HB_INTERNAL static int cmp (const void *pa, const void *pb) { | 
| 292 |     const active_feature_t *a = (const active_feature_t *) pa; | 
| 293 |     const active_feature_t *b = (const active_feature_t *) pb; | 
| 294 |     return a->rec.tagFeature < b->rec.tagFeature ? -1 : a->rec.tagFeature > b->rec.tagFeature ? 1 : | 
| 295 | 	   a->order < b->order ? -1 : a->order > b->order ? 1 : | 
| 296 | 	   a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 1 : | 
| 297 | 	   0; | 
| 298 |   } | 
| 299 |   bool operator== (const active_feature_t *f) | 
| 300 |   { return cmp (this, f) == 0; } | 
| 301 | }; | 
| 302 |  | 
| 303 | struct feature_event_t { | 
| 304 |   unsigned int index; | 
| 305 |   bool start; | 
| 306 |   active_feature_t feature; | 
| 307 |  | 
| 308 |   HB_INTERNAL static int cmp (const void *pa, const void *pb) | 
| 309 |   { | 
| 310 |     const feature_event_t *a = (const feature_event_t *) pa; | 
| 311 |     const feature_event_t *b = (const feature_event_t *) pb; | 
| 312 |     return a->index < b->index ? -1 : a->index > b->index ? 1 : | 
| 313 | 	   a->start < b->start ? -1 : a->start > b->start ? 1 : | 
| 314 | 	   active_feature_t::cmp (&a->feature, &b->feature); | 
| 315 |   } | 
| 316 | }; | 
| 317 |  | 
| 318 | struct range_record_t { | 
| 319 |   TEXTRANGE_PROPERTIES props; | 
| 320 |   unsigned int index_first; /* == start */ | 
| 321 |   unsigned int index_last;  /* == end - 1 */ | 
| 322 | }; | 
| 323 |  | 
| 324 |  | 
| 325 | /* | 
| 326 |  * shaper face data | 
| 327 |  */ | 
| 328 |  | 
| 329 | struct hb_uniscribe_face_data_t { | 
| 330 |   HANDLE fh; | 
| 331 |   hb_uniscribe_shaper_funcs_t *funcs; | 
| 332 |   wchar_t face_name[LF_FACESIZE]; | 
| 333 | }; | 
| 334 |  | 
| 335 | /* face_name should point to a wchar_t[LF_FACESIZE] object. */ | 
| 336 | static void | 
| 337 | _hb_generate_unique_face_name (wchar_t *face_name, unsigned int *plen) | 
| 338 | { | 
| 339 |   /* We'll create a private name for the font from a UUID using a simple, | 
| 340 |    * somewhat base64-like encoding scheme */ | 
| 341 |   const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-" ; | 
| 342 |   UUID id; | 
| 343 |   UuidCreate ((UUID*) &id); | 
| 344 |   static_assert ((2 + 3 * (16/2) < LF_FACESIZE), "" ); | 
| 345 |   unsigned int name_str_len = 0; | 
| 346 |   face_name[name_str_len++] = 'F'; | 
| 347 |   face_name[name_str_len++] = '_'; | 
| 348 |   unsigned char *p = (unsigned char *) &id; | 
| 349 |   for (unsigned int i = 0; i < 16; i += 2) | 
| 350 |   { | 
| 351 |     /* Spread the 16 bits from two bytes of the UUID across three chars of face_name, | 
| 352 |      * using the bits in groups of 5,5,6 to select chars from enc. | 
| 353 |      * This will generate 24 characters; with the 'F_' prefix we already provided, | 
| 354 |      * the name will be 26 chars (plus the NUL terminator), so will always fit within | 
| 355 |      * face_name (LF_FACESIZE = 32). */ | 
| 356 |     face_name[name_str_len++] = enc[p[i] >> 3]; | 
| 357 |     face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f]; | 
| 358 |     face_name[name_str_len++] = enc[p[i + 1] & 0x3f]; | 
| 359 |   } | 
| 360 |   face_name[name_str_len] = 0; | 
| 361 |   if (plen) | 
| 362 |     *plen = name_str_len; | 
| 363 | } | 
| 364 |  | 
| 365 | /* Destroys blob. */ | 
| 366 | static hb_blob_t * | 
| 367 | _hb_rename_font (hb_blob_t *blob, wchar_t *new_name) | 
| 368 | { | 
| 369 |   /* Create a copy of the font data, with the 'name' table replaced by a | 
| 370 |    * table that names the font with our private F_* name created above. | 
| 371 |    * For simplicity, we just append a new 'name' table and update the | 
| 372 |    * sfnt directory; the original table is left in place, but unused. | 
| 373 |    * | 
| 374 |    * The new table will contain just 5 name IDs: family, style, unique, | 
| 375 |    * full, PS. All of them point to the same name data with our unique name. | 
| 376 |    */ | 
| 377 |  | 
| 378 |   blob = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (blob); | 
| 379 |  | 
| 380 |   unsigned int length, new_length, name_str_len; | 
| 381 |   const char *orig_sfnt_data = hb_blob_get_data (blob, &length); | 
| 382 |  | 
| 383 |   _hb_generate_unique_face_name (new_name, &name_str_len); | 
| 384 |  | 
| 385 |   static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 }; | 
| 386 |  | 
| 387 |   unsigned int name_table_length = OT::name::min_size + | 
| 388 | 				   ARRAY_LENGTH (name_IDs) * OT::NameRecord::static_size + | 
| 389 | 				   name_str_len * 2; /* for name data in UTF16BE form */ | 
| 390 |   unsigned int padded_name_table_length = ((name_table_length + 3) & ~3); | 
| 391 |   unsigned int name_table_offset = (length + 3) & ~3; | 
| 392 |  | 
| 393 |   new_length = name_table_offset + padded_name_table_length; | 
| 394 |   void *new_sfnt_data = calloc (1, new_length); | 
| 395 |   if (!new_sfnt_data) | 
| 396 |   { | 
| 397 |     hb_blob_destroy (blob); | 
| 398 |     return nullptr; | 
| 399 |   } | 
| 400 |  | 
| 401 |   memcpy(new_sfnt_data, orig_sfnt_data, length); | 
| 402 |  | 
| 403 |   OT::name &name = StructAtOffset<OT::name> (new_sfnt_data, name_table_offset); | 
| 404 |   name.format = 0; | 
| 405 |   name.count = ARRAY_LENGTH (name_IDs); | 
| 406 |   name.stringOffset = name.get_size (); | 
| 407 |   for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++) | 
| 408 |   { | 
| 409 |     OT::NameRecord &record = name.nameRecordZ[i]; | 
| 410 |     record.platformID = 3; | 
| 411 |     record.encodingID = 1; | 
| 412 |     record.languageID = 0x0409u; /* English */ | 
| 413 |     record.nameID = name_IDs[i]; | 
| 414 |     record.length = name_str_len * 2; | 
| 415 |     record.offset = 0; | 
| 416 |   } | 
| 417 |  | 
| 418 |   /* Copy string data from new_name, converting wchar_t to UTF16BE. */ | 
| 419 |   unsigned char *p = &StructAfter<unsigned char> (name); | 
| 420 |   for (unsigned int i = 0; i < name_str_len; i++) | 
| 421 |   { | 
| 422 |     *p++ = new_name[i] >> 8; | 
| 423 |     *p++ = new_name[i] & 0xff; | 
| 424 |   } | 
| 425 |  | 
| 426 |   /* Adjust name table entry to point to new name table */ | 
| 427 |   const OT::OpenTypeFontFile &file = * (OT::OpenTypeFontFile *) (new_sfnt_data); | 
| 428 |   unsigned int face_count = file.get_face_count (); | 
| 429 |   for (unsigned int face_index = 0; face_index < face_count; face_index++) | 
| 430 |   { | 
| 431 |     /* Note: doing multiple edits (ie. TTC) can be unsafe.  There may be | 
| 432 |      * toe-stepping.  But we don't really care. */ | 
| 433 |     const OT::OpenTypeFontFace &face = file.get_face (face_index); | 
| 434 |     unsigned int index; | 
| 435 |     if (face.find_table_index (HB_OT_TAG_name, &index)) | 
| 436 |     { | 
| 437 |       OT::TableRecord &record = const_cast<OT::TableRecord &> (face.get_table (index)); | 
| 438 |       record.checkSum.set_for_data (&name, padded_name_table_length); | 
| 439 |       record.offset = name_table_offset; | 
| 440 |       record.length = name_table_length; | 
| 441 |     } | 
| 442 |     else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */ | 
| 443 |     { | 
| 444 |       free (new_sfnt_data); | 
| 445 |       hb_blob_destroy (blob); | 
| 446 |       return nullptr; | 
| 447 |     } | 
| 448 |   } | 
| 449 |  | 
| 450 |   /* The checkSumAdjustment field in the 'head' table is now wrong, | 
| 451 |    * but that doesn't actually seem to cause any problems so we don't | 
| 452 |    * bother. */ | 
| 453 |  | 
| 454 |   hb_blob_destroy (blob); | 
| 455 |   return hb_blob_create ((const char *) new_sfnt_data, new_length, | 
| 456 | 			 HB_MEMORY_MODE_WRITABLE, new_sfnt_data, free); | 
| 457 | } | 
| 458 |  | 
| 459 | hb_uniscribe_face_data_t * | 
| 460 | _hb_uniscribe_shaper_face_data_create (hb_face_t *face) | 
| 461 | { | 
| 462 |   hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) calloc (1, sizeof (hb_uniscribe_face_data_t)); | 
| 463 |   if (unlikely (!data)) | 
| 464 |     return nullptr; | 
| 465 |  | 
| 466 |   data->funcs = hb_uniscribe_shaper_get_funcs (); | 
| 467 |   if (unlikely (!data->funcs)) | 
| 468 |   { | 
| 469 |     free (data); | 
| 470 |     return nullptr; | 
| 471 |   } | 
| 472 |  | 
| 473 |   hb_blob_t *blob = hb_face_reference_blob (face); | 
| 474 |   if (unlikely (!hb_blob_get_length (blob))) | 
| 475 |     DEBUG_MSG (UNISCRIBE, face, "Face has empty blob" ); | 
| 476 |  | 
| 477 |   blob = _hb_rename_font (blob, data->face_name); | 
| 478 |   if (unlikely (!blob)) | 
| 479 |   { | 
| 480 |     free (data); | 
| 481 |     return nullptr; | 
| 482 |   } | 
| 483 |  | 
| 484 |   DWORD num_fonts_installed; | 
| 485 |   data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, nullptr), | 
| 486 | 				   hb_blob_get_length (blob), | 
| 487 | 				   0, &num_fonts_installed); | 
| 488 |   if (unlikely (!data->fh)) | 
| 489 |   { | 
| 490 |     DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed" ); | 
| 491 |     free (data); | 
| 492 |     return nullptr; | 
| 493 |   } | 
| 494 |  | 
| 495 |   return data; | 
| 496 | } | 
| 497 |  | 
| 498 | void | 
| 499 | _hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data) | 
| 500 | { | 
| 501 |   RemoveFontMemResourceEx (data->fh); | 
| 502 |   free (data); | 
| 503 | } | 
| 504 |  | 
| 505 |  | 
| 506 | /* | 
| 507 |  * shaper font data | 
| 508 |  */ | 
| 509 |  | 
| 510 | struct hb_uniscribe_font_data_t | 
| 511 | { | 
| 512 |   HDC hdc; | 
| 513 |   mutable LOGFONTW log_font; | 
| 514 |   HFONT hfont; | 
| 515 |   mutable SCRIPT_CACHE script_cache; | 
| 516 |   double x_mult, y_mult; /* From LOGFONT space to HB space. */ | 
| 517 | }; | 
| 518 |  | 
| 519 | static bool | 
| 520 | populate_log_font (LOGFONTW  *lf, | 
| 521 | 		   hb_font_t *font, | 
| 522 | 		   unsigned int font_size) | 
| 523 | { | 
| 524 |   memset (lf, 0, sizeof (*lf)); | 
| 525 |   lf->lfHeight = - (int) font_size; | 
| 526 |   lf->lfCharSet = DEFAULT_CHARSET; | 
| 527 |  | 
| 528 |   memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName)); | 
| 529 |  | 
| 530 |   return true; | 
| 531 | } | 
| 532 |  | 
| 533 | hb_uniscribe_font_data_t * | 
| 534 | _hb_uniscribe_shaper_font_data_create (hb_font_t *font) | 
| 535 | { | 
| 536 |   hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t)); | 
| 537 |   if (unlikely (!data)) | 
| 538 |     return nullptr; | 
| 539 |  | 
| 540 |   int font_size = font->face->get_upem (); /* Default... */ | 
| 541 |   /* No idea if the following is even a good idea. */ | 
| 542 |   if (font->y_ppem) | 
| 543 |     font_size = font->y_ppem; | 
| 544 |  | 
| 545 |   if (font_size < 0) | 
| 546 |     font_size = -font_size; | 
| 547 |   data->x_mult = (double) font->x_scale / font_size; | 
| 548 |   data->y_mult = (double) font->y_scale / font_size; | 
| 549 |  | 
| 550 |   data->hdc = GetDC (nullptr); | 
| 551 |  | 
| 552 |   if (unlikely (!populate_log_font (&data->log_font, font, font_size))) { | 
| 553 |     DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed" ); | 
| 554 |     _hb_uniscribe_shaper_font_data_destroy (data); | 
| 555 |     return nullptr; | 
| 556 |   } | 
| 557 |  | 
| 558 |   data->hfont = CreateFontIndirectW (&data->log_font); | 
| 559 |   if (unlikely (!data->hfont)) { | 
| 560 |     DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed" ); | 
| 561 |     _hb_uniscribe_shaper_font_data_destroy (data); | 
| 562 |      return nullptr; | 
| 563 |   } | 
| 564 |  | 
| 565 |   if (!SelectObject (data->hdc, data->hfont)) { | 
| 566 |     DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed" ); | 
| 567 |     _hb_uniscribe_shaper_font_data_destroy (data); | 
| 568 |      return nullptr; | 
| 569 |   } | 
| 570 |  | 
| 571 |   return data; | 
| 572 | } | 
| 573 |  | 
| 574 | void | 
| 575 | _hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_font_data_t *data) | 
| 576 | { | 
| 577 |   if (data->hdc) | 
| 578 |     ReleaseDC (nullptr, data->hdc); | 
| 579 |   if (data->hfont) | 
| 580 |     DeleteObject (data->hfont); | 
| 581 |   if (data->script_cache) | 
| 582 |     ScriptFreeCache (&data->script_cache); | 
| 583 |   free (data); | 
| 584 | } | 
| 585 |  | 
| 586 | /** | 
| 587 |  * hb_uniscribe_font_get_logfontw: | 
| 588 |  * @font: The #hb_font_t to work upon | 
| 589 |  * | 
| 590 |  * Fetches the LOGFONTW structure that corresponds to the | 
| 591 |  * specified #hb_font_t font. | 
| 592 |  * | 
| 593 |  * Return value: a pointer to the LOGFONTW retrieved | 
| 594 |  * | 
| 595 |  **/ | 
| 596 | LOGFONTW * | 
| 597 | hb_uniscribe_font_get_logfontw (hb_font_t *font) | 
| 598 | { | 
| 599 |   const hb_uniscribe_font_data_t *data =  font->data.uniscribe; | 
| 600 |   return data ? &data->log_font : nullptr; | 
| 601 | } | 
| 602 |  | 
| 603 | /** | 
| 604 |  * hb_uniscribe_font_get_hfont: | 
| 605 |  * @font: The #hb_font_t to work upon | 
| 606 |  * | 
| 607 |  * Fetches the HFONT handle that corresponds to the | 
| 608 |  * specified #hb_font_t font. | 
| 609 |  * | 
| 610 |  * Return value: the HFONT retreieved | 
| 611 |  * | 
| 612 |  **/ | 
| 613 | HFONT | 
| 614 | hb_uniscribe_font_get_hfont (hb_font_t *font) | 
| 615 | { | 
| 616 |   const hb_uniscribe_font_data_t *data =  font->data.uniscribe; | 
| 617 |   return data ? data->hfont : nullptr; | 
| 618 | } | 
| 619 |  | 
| 620 |  | 
| 621 | /* | 
| 622 |  * shaper | 
| 623 |  */ | 
| 624 |  | 
| 625 |  | 
| 626 | hb_bool_t | 
| 627 | _hb_uniscribe_shape (hb_shape_plan_t    *shape_plan, | 
| 628 | 		     hb_font_t          *font, | 
| 629 | 		     hb_buffer_t        *buffer, | 
| 630 | 		     const hb_feature_t *features, | 
| 631 | 		     unsigned int        num_features) | 
| 632 | { | 
| 633 |   hb_face_t *face = font->face; | 
| 634 |   const hb_uniscribe_face_data_t *face_data = face->data.uniscribe; | 
| 635 |   const hb_uniscribe_font_data_t *font_data = font->data.uniscribe; | 
| 636 |   hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs; | 
| 637 |  | 
| 638 |   /* | 
| 639 |    * Set up features. | 
| 640 |    */ | 
| 641 |   hb_vector_t<OPENTYPE_FEATURE_RECORD> feature_records; | 
| 642 |   hb_vector_t<range_record_t> range_records; | 
| 643 |   if (num_features) | 
| 644 |   { | 
| 645 |     /* Sort features by start/end events. */ | 
| 646 |     hb_vector_t<feature_event_t> feature_events; | 
| 647 |     for (unsigned int i = 0; i < num_features; i++) | 
| 648 |     { | 
| 649 |       active_feature_t feature; | 
| 650 |       feature.rec.tagFeature = hb_uint32_swap (features[i].tag); | 
| 651 |       feature.rec.lParameter = features[i].value; | 
| 652 |       feature.order = i; | 
| 653 |  | 
| 654 |       feature_event_t *event; | 
| 655 |  | 
| 656 |       event = feature_events.push (); | 
| 657 |       event->index = features[i].start; | 
| 658 |       event->start = true; | 
| 659 |       event->feature = feature; | 
| 660 |  | 
| 661 |       event = feature_events.push (); | 
| 662 |       event->index = features[i].end; | 
| 663 |       event->start = false; | 
| 664 |       event->feature = feature; | 
| 665 |     } | 
| 666 |     feature_events.qsort (); | 
| 667 |     /* Add a strategic final event. */ | 
| 668 |     { | 
| 669 |       active_feature_t feature; | 
| 670 |       feature.rec.tagFeature = 0; | 
| 671 |       feature.rec.lParameter = 0; | 
| 672 |       feature.order = num_features + 1; | 
| 673 |  | 
| 674 |       feature_event_t *event = feature_events.push (); | 
| 675 |       event->index = 0; /* This value does magic. */ | 
| 676 |       event->start = false; | 
| 677 |       event->feature = feature; | 
| 678 |     } | 
| 679 |  | 
| 680 |     /* Scan events and save features for each range. */ | 
| 681 |     hb_vector_t<active_feature_t> active_features; | 
| 682 |     unsigned int last_index = 0; | 
| 683 |     for (unsigned int i = 0; i < feature_events.length; i++) | 
| 684 |     { | 
| 685 |       feature_event_t *event = &feature_events[i]; | 
| 686 |  | 
| 687 |       if (event->index != last_index) | 
| 688 |       { | 
| 689 | 	/* Save a snapshot of active features and the range. */ | 
| 690 | 	range_record_t *range = range_records.push (); | 
| 691 |  | 
| 692 | 	unsigned int offset = feature_records.length; | 
| 693 |  | 
| 694 | 	active_features.qsort (); | 
| 695 | 	for (unsigned int j = 0; j < active_features.length; j++) | 
| 696 | 	{ | 
| 697 | 	  if (!j || active_features[j].rec.tagFeature != feature_records[feature_records.length - 1].tagFeature) | 
| 698 | 	  { | 
| 699 | 	    feature_records.push (active_features[j].rec); | 
| 700 | 	  } | 
| 701 | 	  else | 
| 702 | 	  { | 
| 703 | 	    /* Overrides value for existing feature. */ | 
| 704 | 	    feature_records[feature_records.length - 1].lParameter = active_features[j].rec.lParameter; | 
| 705 | 	  } | 
| 706 | 	} | 
| 707 |  | 
| 708 | 	/* Will convert to pointer after all is ready, since feature_records.array | 
| 709 | 	 * may move as we grow it. */ | 
| 710 | 	range->props.potfRecords = reinterpret_cast<OPENTYPE_FEATURE_RECORD *> (offset); | 
| 711 | 	range->props.cotfRecords = feature_records.length - offset; | 
| 712 | 	range->index_first = last_index; | 
| 713 | 	range->index_last  = event->index - 1; | 
| 714 |  | 
| 715 | 	last_index = event->index; | 
| 716 |       } | 
| 717 |  | 
| 718 |       if (event->start) | 
| 719 |       { | 
| 720 | 	active_features.push (event->feature); | 
| 721 |       } | 
| 722 |       else | 
| 723 |       { | 
| 724 | 	active_feature_t *feature = active_features.find (&event->feature); | 
| 725 | 	if (feature) | 
| 726 | 	  active_features.remove (feature - active_features.arrayZ); | 
| 727 |       } | 
| 728 |     } | 
| 729 |  | 
| 730 |     if (!range_records.length) /* No active feature found. */ | 
| 731 |       num_features = 0; | 
| 732 |  | 
| 733 |     /* Fixup the pointers. */ | 
| 734 |     for (unsigned int i = 0; i < range_records.length; i++) | 
| 735 |     { | 
| 736 |       range_record_t *range = &range_records[i]; | 
| 737 |       range->props.potfRecords = (OPENTYPE_FEATURE_RECORD *) feature_records + reinterpret_cast<uintptr_t> (range->props.potfRecords); | 
| 738 |     } | 
| 739 |   } | 
| 740 |  | 
| 741 | #define FAIL(...) \ | 
| 742 |   HB_STMT_START { \ | 
| 743 |     DEBUG_MSG (UNISCRIBE, nullptr, __VA_ARGS__); \ | 
| 744 |     return false; \ | 
| 745 |   } HB_STMT_END | 
| 746 |  | 
| 747 |   HRESULT hr; | 
| 748 |  | 
| 749 | retry: | 
| 750 |  | 
| 751 |   unsigned int scratch_size; | 
| 752 |   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); | 
| 753 |  | 
| 754 | #define ALLOCATE_ARRAY(Type, name, len) \ | 
| 755 |   Type *name = (Type *) scratch; \ | 
| 756 |   do { \ | 
| 757 |     unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ | 
| 758 |     assert (_consumed <= scratch_size); \ | 
| 759 |     scratch += _consumed; \ | 
| 760 |     scratch_size -= _consumed; \ | 
| 761 |   } while (0) | 
| 762 |  | 
| 763 | #define utf16_index() var1.u32 | 
| 764 |  | 
| 765 |   ALLOCATE_ARRAY (WCHAR, pchars, buffer->len * 2); | 
| 766 |  | 
| 767 |   unsigned int chars_len = 0; | 
| 768 |   for (unsigned int i = 0; i < buffer->len; i++) | 
| 769 |   { | 
| 770 |     hb_codepoint_t c = buffer->info[i].codepoint; | 
| 771 |     buffer->info[i].utf16_index() = chars_len; | 
| 772 |     if (likely (c <= 0xFFFFu)) | 
| 773 |       pchars[chars_len++] = c; | 
| 774 |     else if (unlikely (c > 0x10FFFFu)) | 
| 775 |       pchars[chars_len++] = 0xFFFDu; | 
| 776 |     else { | 
| 777 |       pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); | 
| 778 |       pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); | 
| 779 |     } | 
| 780 |   } | 
| 781 |  | 
| 782 |   ALLOCATE_ARRAY (WORD, log_clusters, chars_len); | 
| 783 |   ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len); | 
| 784 |  | 
| 785 |   if (num_features) | 
| 786 |   { | 
| 787 |     /* Need log_clusters to assign features. */ | 
| 788 |     chars_len = 0; | 
| 789 |     for (unsigned int i = 0; i < buffer->len; i++) | 
| 790 |     { | 
| 791 |       hb_codepoint_t c = buffer->info[i].codepoint; | 
| 792 |       unsigned int cluster = buffer->info[i].cluster; | 
| 793 |       log_clusters[chars_len++] = cluster; | 
| 794 |       if (hb_in_range (c, 0x10000u, 0x10FFFFu)) | 
| 795 | 	log_clusters[chars_len++] = cluster; /* Surrogates. */ | 
| 796 |     } | 
| 797 |   } | 
| 798 |  | 
| 799 |   /* The -2 in the following is to compensate for possible | 
| 800 |    * alignment needed after the WORD array.  sizeof(WORD) == 2. */ | 
| 801 |   unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) | 
| 802 | 			   / (sizeof (WORD) + | 
| 803 | 			      sizeof (SCRIPT_GLYPHPROP) + | 
| 804 | 			      sizeof (int) + | 
| 805 | 			      sizeof (GOFFSET) + | 
| 806 | 			      sizeof (uint32_t)); | 
| 807 |  | 
| 808 |   ALLOCATE_ARRAY (WORD, glyphs, glyphs_size); | 
| 809 |   ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size); | 
| 810 |   ALLOCATE_ARRAY (int, advances, glyphs_size); | 
| 811 |   ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size); | 
| 812 |   ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); | 
| 813 |  | 
| 814 |   /* Note: | 
| 815 |    * We can't touch the contents of glyph_props.  Our fallback | 
| 816 |    * implementations of Shape and Place functions use that buffer | 
| 817 |    * by casting it to a different type.  It works because they | 
| 818 |    * both agree about it, but if we want to access it here we | 
| 819 |    * need address that issue first. | 
| 820 |    */ | 
| 821 |  | 
| 822 | #undef ALLOCATE_ARRAY | 
| 823 |  | 
| 824 | #define MAX_ITEMS 256 | 
| 825 |  | 
| 826 |   SCRIPT_ITEM items[MAX_ITEMS + 1]; | 
| 827 |   SCRIPT_CONTROL bidi_control = {0}; | 
| 828 |   SCRIPT_STATE bidi_state = {0}; | 
| 829 |   ULONG script_tags[MAX_ITEMS]; | 
| 830 |   int item_count; | 
| 831 |  | 
| 832 |   /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */ | 
| 833 |   //bidi_control.fMergeNeutralItems = true; | 
| 834 |   *(uint32_t*)&bidi_control |= 1u<<24; | 
| 835 |  | 
| 836 |   bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; | 
| 837 |   bidi_state.fOverrideDirection = 1; | 
| 838 |  | 
| 839 |   hr = funcs->ScriptItemizeOpenType (pchars, | 
| 840 | 				     chars_len, | 
| 841 | 				     MAX_ITEMS, | 
| 842 | 				     &bidi_control, | 
| 843 | 				     &bidi_state, | 
| 844 | 				     items, | 
| 845 | 				     script_tags, | 
| 846 | 				     &item_count); | 
| 847 |   if (unlikely (FAILED (hr))) | 
| 848 |     FAIL ("ScriptItemizeOpenType() failed: 0x%08lx" , hr); | 
| 849 |  | 
| 850 | #undef MAX_ITEMS | 
| 851 |  | 
| 852 |   hb_tag_t lang_tag; | 
| 853 |   unsigned int lang_count = 1; | 
| 854 |   hb_ot_tags_from_script_and_language (buffer->props.script, | 
| 855 | 				       buffer->props.language, | 
| 856 | 				       nullptr, nullptr, | 
| 857 | 				       &lang_count, &lang_tag); | 
| 858 |   OPENTYPE_TAG language_tag = hb_uint32_swap (lang_count ? lang_tag : HB_TAG_NONE); | 
| 859 |   hb_vector_t<TEXTRANGE_PROPERTIES*> range_properties; | 
| 860 |   hb_vector_t<int> range_char_counts; | 
| 861 |  | 
| 862 |   unsigned int glyphs_offset = 0; | 
| 863 |   unsigned int glyphs_len; | 
| 864 |   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); | 
| 865 |   for (int i = 0; i < item_count; i++) | 
| 866 |   { | 
| 867 |     unsigned int chars_offset = items[i].iCharPos; | 
| 868 |     unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset; | 
| 869 |  | 
| 870 |     if (num_features) | 
| 871 |     { | 
| 872 |       range_properties.shrink (0); | 
| 873 |       range_char_counts.shrink (0); | 
| 874 |  | 
| 875 |       range_record_t *last_range = &range_records[0]; | 
| 876 |  | 
| 877 |       for (unsigned int k = chars_offset; k < chars_offset + item_chars_len; k++) | 
| 878 |       { | 
| 879 | 	range_record_t *range = last_range; | 
| 880 | 	while (log_clusters[k] < range->index_first) | 
| 881 | 	  range--; | 
| 882 | 	while (log_clusters[k] > range->index_last) | 
| 883 | 	  range++; | 
| 884 | 	if (!range_properties.length || | 
| 885 | 	    &range->props != range_properties[range_properties.length - 1]) | 
| 886 | 	{ | 
| 887 | 	  TEXTRANGE_PROPERTIES **props = range_properties.push (); | 
| 888 | 	  int *c = range_char_counts.push (); | 
| 889 | 	  if (unlikely (!props || !c)) | 
| 890 | 	  { | 
| 891 | 	    range_properties.shrink (0); | 
| 892 | 	    range_char_counts.shrink (0); | 
| 893 | 	    break; | 
| 894 | 	  } | 
| 895 | 	  *props = &range->props; | 
| 896 | 	  *c = 1; | 
| 897 | 	} | 
| 898 | 	else | 
| 899 | 	{ | 
| 900 | 	  range_char_counts[range_char_counts.length - 1]++; | 
| 901 | 	} | 
| 902 |  | 
| 903 | 	last_range = range; | 
| 904 |       } | 
| 905 |     } | 
| 906 |  | 
| 907 |     /* Asking for glyphs in logical order circumvents at least | 
| 908 |      * one bug in Uniscribe. */ | 
| 909 |     items[i].a.fLogicalOrder = true; | 
| 910 |  | 
| 911 |   retry_shape: | 
| 912 |     hr = funcs->ScriptShapeOpenType (font_data->hdc, | 
| 913 | 				     &font_data->script_cache, | 
| 914 | 				     &items[i].a, | 
| 915 | 				     script_tags[i], | 
| 916 | 				     language_tag, | 
| 917 | 				     range_char_counts.arrayZ, | 
| 918 | 				     range_properties.arrayZ, | 
| 919 | 				     range_properties.length, | 
| 920 | 				     pchars + chars_offset, | 
| 921 | 				     item_chars_len, | 
| 922 | 				     glyphs_size - glyphs_offset, | 
| 923 | 				     /* out */ | 
| 924 | 				     log_clusters + chars_offset, | 
| 925 | 				     char_props + chars_offset, | 
| 926 | 				     glyphs + glyphs_offset, | 
| 927 | 				     glyph_props + glyphs_offset, | 
| 928 | 				     (int *) &glyphs_len); | 
| 929 |  | 
| 930 |     if (unlikely (items[i].a.fNoGlyphIndex)) | 
| 931 |       FAIL ("ScriptShapeOpenType() set fNoGlyphIndex" ); | 
| 932 |     if (unlikely (hr == E_OUTOFMEMORY || hr == E_NOT_SUFFICIENT_BUFFER)) | 
| 933 |     { | 
| 934 |       if (unlikely (!buffer->ensure (buffer->allocated * 2))) | 
| 935 | 	FAIL ("Buffer resize failed" ); | 
| 936 |       goto retry; | 
| 937 |     } | 
| 938 |     if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT)) | 
| 939 |     { | 
| 940 |       if (items[i].a.eScript == SCRIPT_UNDEFINED) | 
| 941 | 	FAIL ("ScriptShapeOpenType() failed: Font doesn't support script" ); | 
| 942 |       items[i].a.eScript = SCRIPT_UNDEFINED; | 
| 943 |       goto retry_shape; | 
| 944 |     } | 
| 945 |     if (unlikely (FAILED (hr))) | 
| 946 |     { | 
| 947 |       FAIL ("ScriptShapeOpenType() failed: 0x%08lx" , hr); | 
| 948 |     } | 
| 949 |  | 
| 950 |     for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++) | 
| 951 |       log_clusters[j] += glyphs_offset; | 
| 952 |  | 
| 953 |     hr = funcs->ScriptPlaceOpenType (font_data->hdc, | 
| 954 | 				     &font_data->script_cache, | 
| 955 | 				     &items[i].a, | 
| 956 | 				     script_tags[i], | 
| 957 | 				     language_tag, | 
| 958 | 				     range_char_counts.arrayZ, | 
| 959 | 				     range_properties.arrayZ, | 
| 960 | 				     range_properties.length, | 
| 961 | 				     pchars + chars_offset, | 
| 962 | 				     log_clusters + chars_offset, | 
| 963 | 				     char_props + chars_offset, | 
| 964 | 				     item_chars_len, | 
| 965 | 				     glyphs + glyphs_offset, | 
| 966 | 				     glyph_props + glyphs_offset, | 
| 967 | 				     glyphs_len, | 
| 968 | 				     /* out */ | 
| 969 | 				     advances + glyphs_offset, | 
| 970 | 				     offsets + glyphs_offset, | 
| 971 | 				     nullptr); | 
| 972 |     if (unlikely (FAILED (hr))) | 
| 973 |       FAIL ("ScriptPlaceOpenType() failed: 0x%08lx" , hr); | 
| 974 |  | 
| 975 |     if (DEBUG_ENABLED (UNISCRIBE)) | 
| 976 |       fprintf (stderr, "Item %d RTL %d LayoutRTL %d LogicalOrder %d ScriptTag %c%c%c%c\n" , | 
| 977 | 	       i, | 
| 978 | 	       items[i].a.fRTL, | 
| 979 | 	       items[i].a.fLayoutRTL, | 
| 980 | 	       items[i].a.fLogicalOrder, | 
| 981 | 	       HB_UNTAG (hb_uint32_swap (script_tags[i]))); | 
| 982 |  | 
| 983 |     glyphs_offset += glyphs_len; | 
| 984 |   } | 
| 985 |   glyphs_len = glyphs_offset; | 
| 986 |  | 
| 987 |   /* Ok, we've got everything we need, now compose output buffer, | 
| 988 |    * very, *very*, carefully! */ | 
| 989 |  | 
| 990 |   /* Calculate visual-clusters.  That's what we ship. */ | 
| 991 |   for (unsigned int i = 0; i < glyphs_len; i++) | 
| 992 |     vis_clusters[i] = (uint32_t) -1; | 
| 993 |   for (unsigned int i = 0; i < buffer->len; i++) { | 
| 994 |     uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]]; | 
| 995 |     *p = hb_min (*p, buffer->info[i].cluster); | 
| 996 |   } | 
| 997 |   for (unsigned int i = 1; i < glyphs_len; i++) | 
| 998 |     if (vis_clusters[i] == (uint32_t) -1) | 
| 999 |       vis_clusters[i] = vis_clusters[i - 1]; | 
| 1000 |  | 
| 1001 | #undef utf16_index | 
| 1002 |  | 
| 1003 |   if (unlikely (!buffer->ensure (glyphs_len))) | 
| 1004 |     FAIL ("Buffer in error" ); | 
| 1005 |  | 
| 1006 | #undef FAIL | 
| 1007 |  | 
| 1008 |   /* Set glyph infos */ | 
| 1009 |   buffer->len = 0; | 
| 1010 |   for (unsigned int i = 0; i < glyphs_len; i++) | 
| 1011 |   { | 
| 1012 |     hb_glyph_info_t *info = &buffer->info[buffer->len++]; | 
| 1013 |  | 
| 1014 |     info->codepoint = glyphs[i]; | 
| 1015 |     info->cluster = vis_clusters[i]; | 
| 1016 |  | 
| 1017 |     /* The rest is crap.  Let's store position info there for now. */ | 
| 1018 |     info->mask = advances[i]; | 
| 1019 |     info->var1.i32 = offsets[i].du; | 
| 1020 |     info->var2.i32 = offsets[i].dv; | 
| 1021 |   } | 
| 1022 |  | 
| 1023 |   /* Set glyph positions */ | 
| 1024 |   buffer->clear_positions (); | 
| 1025 |   double x_mult = font_data->x_mult, y_mult = font_data->y_mult; | 
| 1026 |   for (unsigned int i = 0; i < glyphs_len; i++) | 
| 1027 |   { | 
| 1028 |     hb_glyph_info_t *info = &buffer->info[i]; | 
| 1029 |     hb_glyph_position_t *pos = &buffer->pos[i]; | 
| 1030 |  | 
| 1031 |     /* TODO vertical */ | 
| 1032 |     pos->x_advance = x_mult * (int32_t) info->mask; | 
| 1033 |     pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32); | 
| 1034 |     pos->y_offset = y_mult * info->var2.i32; | 
| 1035 |   } | 
| 1036 |  | 
| 1037 |   if (backward) | 
| 1038 |     hb_buffer_reverse (buffer); | 
| 1039 |  | 
| 1040 |   buffer->unsafe_to_break_all (); | 
| 1041 |  | 
| 1042 |   /* Wow, done! */ | 
| 1043 |   return true; | 
| 1044 | } | 
| 1045 |  | 
| 1046 |  | 
| 1047 | #endif | 
| 1048 |  |