bidi.c 21.8 KB
Newer Older
1 2 3 4
/*
 * GDI BiDirectional handling
 *
 * Copyright 2003 Shachar Shemesh
5
 * Copyright 2007 Maarten Lankhorst
6
 * Copyright 2010 CodeWeavers, Aric Stewart
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 *
 * Code derived from the modified reference implementation
 * that was found in revision 17 of http://unicode.org/reports/tr9/
 * "Unicode Standard Annex #9: THE BIDIRECTIONAL ALGORITHM"
 *
 * -- Copyright (C) 1999-2005, ASMUS, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of the Unicode data files and any associated documentation (the
 * "Data Files") or Unicode software and any associated documentation (the
 * "Software") to deal in the Data Files or Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, and/or sell copies of the Data Files or Software,
 * and to permit persons to whom the Data Files or Software are furnished
 * to do so, provided that (a) the above copyright notice(s) and this
 * permission notice appear with all copies of the Data Files or Software,
 * (b) both the above copyright notice(s) and this permission notice appear
 * in associated documentation, and (c) there is clear notice in each
 * modified Data File or in the Software as well as in the documentation
 * associated with the Data File(s) or Software that the data or software
 * has been modified.
42 43 44 45
 */

#include "config.h"

46 47
#include <stdarg.h>
#include "windef.h"
48 49
#include "winbase.h"
#include "wingdi.h"
50
#include "winnls.h"
51
#include "usp10.h"
52
#include "wine/debug.h"
53
#include "gdi_private.h"
54 55 56

WINE_DEFAULT_DEBUG_CHANNEL(bidi);

57 58 59 60
/* HELPER FUNCTIONS AND DECLARATIONS */

#define odd(x) ((x) & 1)

61 62
extern const unsigned short bidi_direction_table[] DECLSPEC_HIDDEN;

63 64 65 66 67 68 69 70
/*------------------------------------------------------------------------
    Bidirectional Character Types

    as defined by the Unicode Bidirectional Algorithm Table 3-7.

    Note:

      The list of bidirectional character types here is not grouped the
71
      same way as the table 3-7, since the numeric values for the types
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
      are chosen to keep the state and action tables compact.
------------------------------------------------------------------------*/
enum directions
{
    /* input types */
             /* ON MUST be zero, code relies on ON = N = 0 */
    ON = 0,  /* Other Neutral */
    L,       /* Left Letter */
    R,       /* Right Letter */
    AN,      /* Arabic Number */
    EN,      /* European Number */
    AL,      /* Arabic Letter (Right-to-left) */
    NSM,     /* Non-spacing Mark */
    CS,      /* Common Separator */
    ES,      /* European Separator */
    ET,      /* European Terminator (post/prefix e.g. $ and %) */

    /* resolved types */
    BN,      /* Boundary neutral (type of RLE etc after explicit levels) */

    /* input types, */
    S,       /* Segment Separator (TAB)        // used only in L1 */
    WS,      /* White space                    // used only in L1 */
    B,       /* Paragraph Separator (aka as PS) */

    /* types for explicit controls */
    RLO,     /* these are used only in X1-X9 */
    RLE,
    LRO,
    LRE,
    PDF,

104 105 106 107 108
    LRI, /* Isolate formatting characters new with 6.3 */
    RLI,
    FSI,
    PDI,

109
    /* resolved types, also resolved directions */
110
    NI = ON,  /* alias, where ON, WS and S are treated the same */
111 112 113 114
};

/* HELPER FUNCTIONS */

115 116 117 118 119
static inline unsigned short get_table_entry(const unsigned short *table, WCHAR ch)
{
    return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
}

120
/* Convert the libwine information to the direction enum */
121 122
static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
{
123 124
    unsigned i;

125
    for (i = 0; i < uCount; ++i)
126
        chartype[i] = get_table_entry( bidi_direction_table, lpString[i] );
127 128
}

129 130
/* Set a run of cval values at locations all prior to, but not including */
/* iStart, to the new value nval. */
131
static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
132 133 134 135 136 137 138 139
{
    int i = iStart - 1;
    for (; i >= iStart - cval; i--)
    {
        pval[i] = nval;
    }
}

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
/* THE PARAGRAPH LEVEL */

/*------------------------------------------------------------------------
    Function: resolveParagraphs

    Resolves the input strings into blocks over which the algorithm
    is then applied.

    Implements Rule P1 of the Unicode Bidi Algorithm

    Input: Text string
           Character count

    Output: revised character count

155
    Note:    This is a very simplistic function. In effect it restricts
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
            the action of the algorithm to the first paragraph in the input
            where a paragraph ends at the end of the first block separator
            or at the end of the input text.

------------------------------------------------------------------------*/

static int resolveParagraphs(WORD *types, int cch)
{
    /* skip characters not of type B */
    int ich = 0;
    for(; ich < cch && types[ich] != B; ich++);
    /* stop after first B, make it a BN for use in the next steps */
    if (ich < cch && types[ich] == B)
        types[ich++] = BN;
    return ich;
}

173 174 175 176 177 178
/* REORDER */
/*------------------------------------------------------------------------
    Function: resolveLines

    Breaks a paragraph into lines

179 180
    Input:  Array of line break flags
            Character count
181 182 183 184 185 186 187 188 189
    In/Out: Array of characters

    Returns the count of characters on the first line

    Note: This function only breaks lines at hard line breaks. Other
    line breaks can be passed in. If pbrk[n] is TRUE, then a break
    occurs after the character in pszInput[n]. Breaks before the first
    character are not allowed.
------------------------------------------------------------------------*/
190
static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
{
    /* skip characters not of type LS */
    int ich = 0;
    for(; ich < cch; ich++)
    {
        if (pszInput[ich] == (WCHAR)'\n' || (pbrk && pbrk[ich]))
        {
            ich++;
            break;
        }
    }

    return ich;
}

/*------------------------------------------------------------------------
    Function: resolveWhiteSpace

    Resolves levels for WS and S
    Implements rule L1 of the Unicode bidi Algorithm.

    Input:  Base embedding level
            Character count
            Array of direction classes (for one line of text)

    In/Out: Array of embedding levels (for one line of text)

    Note: this should be applied a line at a time. The default driver
          code supplied in this file assumes a single line of text; for
          a real implementation, cch and the initial pointer values
          would have to be adjusted.
------------------------------------------------------------------------*/
223
static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
224 225
{
    int cchrun = 0;
226
    BYTE oldlevel = baselevel;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

    int ich = 0;
    for (; ich < cch; ich++)
    {
        switch(pcls[ich])
        {
        default:
            cchrun = 0; /* any other character breaks the run */
            break;
        case WS:
            cchrun++;
            break;

        case RLE:
        case LRE:
        case LRO:
        case RLO:
        case PDF:
245 246 247 248
        case LRI:
        case RLI:
        case FSI:
        case PDI:
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
        case BN:
            plevel[ich] = oldlevel;
            cchrun++;
            break;

        case S:
        case B:
            /* reset levels for WS before eot */
            SetDeferredRun(plevel, cchrun, ich, baselevel);
            cchrun = 0;
            plevel[ich] = baselevel;
            break;
        }
        oldlevel = plevel[ich];
    }
    /* reset level before eot */
    SetDeferredRun(plevel, cchrun, ich, baselevel);
}

/*------------------------------------------------------------------------
    Function: BidiLines

    Implements the Line-by-Line phases of the Unicode Bidi Algorithm

      Input:     Count of characters
274
                 Array of character directions
275 276 277 278 279

    Inp/Out: Input text
             Array of levels

------------------------------------------------------------------------*/
280 281
static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
                      BYTE * plevelLine, int cchPara, const BOOL * pbrk)
282 283
{
    int cchLine = 0;
284 285 286 287 288 289 290 291 292
    int done = 0;
    int *run;

    run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
    if (!run)
    {
        WARN("Out of memory\n");
        return;
    }
293 294 295 296 297 298 299 300 301

    do
    {
        /* break lines at LS */
        cchLine = resolveLines(pszLine, pbrk, cchPara);

        /* resolve whitespace */
        resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);

302 303
        if (pszOutLine)
        {
304
            int i;
305
            /* reorder each line in place */
306
            ScriptLayout(cchLine, plevelLine, NULL, run);
307 308
            for (i = 0; i < cchLine; i++)
                pszOutLine[done+run[i]] = pszLine[i];
309
        }
310 311 312 313 314 315

        pszLine += cchLine;
        plevelLine += cchLine;
        pbrk += pbrk ? cchLine : 0;
        pclsLine += cchLine;
        cchPara -= cchLine;
316
        done += cchLine;
317 318

    } while (cchPara);
319 320

    HeapFree(GetProcessHeap(), 0, run);
321 322
}

323 324
/*************************************************************
 *    BIDI_Reorder
325 326
 *
 *     Returns TRUE if reordering was required and done.
327 328
 */
BOOL BIDI_Reorder(
329
                HDC hDC,        /*[in] Display DC */
330 331 332 333 334 335
                LPCWSTR lpString,       /* [in] The string for which information is to be returned */
                INT uCount,     /* [in] Number of WCHARs in string. */
                DWORD dwFlags,  /* [in] GetCharacterPlacement compatible flags specifying how to process the string */
                DWORD dwWineGCP_Flags,       /* [in] Wine internal flags - Force paragraph direction */
                LPWSTR lpOutString, /* [out] Reordered string */
                INT uCountOut,  /* [in] Size of output buffer */
336
                UINT *lpOrder, /* [out] Logical -> Visual order map */
337 338
                WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
                INT *cGlyphs /* [out] number of glyphs generated */
339 340
    )
{
341
    WORD *chartype;
342
    BYTE *levels;
343 344
    INT i, done;
    unsigned glyph_i;
345
    BOOL is_complex;
346 347 348 349 350 351 352

    int maxItems;
    int nItems;
    SCRIPT_CONTROL Control;
    SCRIPT_STATE State;
    SCRIPT_ITEM *pItems;
    HRESULT res;
353 354 355 356 357 358
    SCRIPT_CACHE psc = NULL;
    WORD *run_glyphs = NULL;
    WORD *pwLogClust = NULL;
    SCRIPT_VISATTR *psva = NULL;
    DWORD cMaxGlyphs = 0;
    BOOL  doGlyphs = TRUE;
359

360
    TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
361 362
          debugstr_wn(lpString, uCount), uCount, dwFlags,
          lpOutString, lpOrder);
363

364 365
    memset(&Control, 0, sizeof(Control));
    memset(&State, 0, sizeof(State));
366 367
    if (lpGlyphs)
        *lpGlyphs = NULL;
368

369 370 371 372
    if (!(dwFlags & GCP_REORDER))
    {
        FIXME("Asked to reorder without reorder flag set\n");
        return FALSE;
373
    }
374

375
    if (lpOutString && uCountOut < uCount)
376
    {
377
        FIXME("lpOutString too small\n");
378 379 380
        return FALSE;
    }

381
    chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
382 383 384 385 386 387
    if (!chartype)
    {
        WARN("Out of memory\n");
        return FALSE;
    }

388 389
    if (lpOutString)
        memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
390

391 392 393 394 395 396 397 398 399
    is_complex = FALSE;
    for (i = 0; i < uCount && !is_complex; i++)
    {
        if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
            (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
            (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
            is_complex = TRUE;
    }

400 401 402 403
    /* Verify reordering will be required */
    if ((WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags&WINE_GCPW_DIR_MASK)) ||
        ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL))
        State.uBidiLevel = 1;
404
    else if (!is_complex)
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
    {
        done = 1;
        classify(lpString, chartype, uCount);
        for (i = 0; i < uCount; i++)
            switch (chartype[i])
            {
                case R:
                case AL:
                case RLE:
                case RLO:
                    done = 0;
                    break;
            }
        if (done)
        {
            HeapFree(GetProcessHeap(), 0, chartype);
421 422 423 424 425
            if (lpOrder)
            {
                for (i = 0; i < uCount; i++)
                    lpOrder[i] = i;
            }
426 427 428 429
            return TRUE;
        }
    }

430
    levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    if (!levels)
    {
        WARN("Out of memory\n");
        HeapFree(GetProcessHeap(), 0, chartype);
        return FALSE;
    }

    maxItems = 5;
    pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
    if (!pItems)
    {
        WARN("Out of memory\n");
        HeapFree(GetProcessHeap(), 0, chartype);
        HeapFree(GetProcessHeap(), 0, levels);
        return FALSE;
    }
447

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
    if (lpGlyphs)
    {
        cMaxGlyphs = 1.5 * uCount + 16;
        run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
        if (!run_glyphs)
        {
            WARN("Out of memory\n");
            HeapFree(GetProcessHeap(), 0, chartype);
            HeapFree(GetProcessHeap(), 0, levels);
            HeapFree(GetProcessHeap(), 0, pItems);
            return FALSE;
        }
        pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
        if (!pwLogClust)
        {
            WARN("Out of memory\n");
            HeapFree(GetProcessHeap(), 0, chartype);
            HeapFree(GetProcessHeap(), 0, levels);
            HeapFree(GetProcessHeap(), 0, pItems);
            HeapFree(GetProcessHeap(), 0, run_glyphs);
            return FALSE;
        }
        psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * uCount);
        if (!psva)
        {
            WARN("Out of memory\n");
            HeapFree(GetProcessHeap(), 0, chartype);
            HeapFree(GetProcessHeap(), 0, levels);
            HeapFree(GetProcessHeap(), 0, pItems);
            HeapFree(GetProcessHeap(), 0, run_glyphs);
            HeapFree(GetProcessHeap(), 0, pwLogClust);
            return FALSE;
        }
    }

483
    done = 0;
484
    glyph_i = 0;
485 486
    while (done < uCount)
    {
487
        INT j;
488
        classify(lpString + done, chartype, uCount - done);
489 490
        /* limit text to first block */
        i = resolveParagraphs(chartype, uCount - done);
491
        for (j = 0; j < i; ++j)
492 493 494 495 496
            switch(chartype[j])
            {
                case B:
                case S:
                case WS:
497
                case ON: chartype[j] = NI;
498 499 500
                default: continue;
            }

501
        if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL)
502
            State.uBidiLevel = 1;
503
        else if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_LTR)
504
            State.uBidiLevel = 0;
505

506
        if (dwWineGCP_Flags & WINE_GCPW_LOOSE_MASK)
507
        {
508 509 510
            for (j = 0; j < i; ++j)
                if (chartype[j] == L)
                {
511
                    State.uBidiLevel = 0;
512 513
                    break;
                }
514
                else if (chartype[j] == R || chartype[j] == AL)
515
                {
516
                    State.uBidiLevel = 1;
517 518
                    break;
                }
519
        }
520

521 522 523 524 525 526 527 528 529 530
        res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
        while (res == E_OUTOFMEMORY)
        {
            maxItems = maxItems * 2;
            pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(SCRIPT_ITEM) * maxItems);
            if (!pItems)
            {
                WARN("Out of memory\n");
                HeapFree(GetProcessHeap(), 0, chartype);
                HeapFree(GetProcessHeap(), 0, levels);
531 532 533
                HeapFree(GetProcessHeap(), 0, run_glyphs);
                HeapFree(GetProcessHeap(), 0, pwLogClust);
                HeapFree(GetProcessHeap(), 0, psva);
534 535 536 537
                return FALSE;
            }
            res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
        }
538

539 540 541 542 543 544 545
        if (lpOutString || lpOrder)
            for (j = 0; j < nItems; j++)
            {
                int k;
                for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
                    levels[k] = pItems[j].a.s.uBidiLevel;
            }
546

547 548 549 550
        if (lpOutString)
        {
            /* assign directional types again, but for WS, S this time */
            classify(lpString + done, chartype, i);
551

552 553 554
            BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
                        chartype, levels, i, 0);
        }
555

556 557
        if (lpOrder)
        {
558
            int k, lastgood;
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
            for (j = lastgood = 0; j < i; ++j)
                if (levels[j] != levels[lastgood])
                {
                    --j;
                    if (odd(levels[lastgood]))
                        for (k = j; k >= lastgood; --k)
                            lpOrder[done + k] = done + j - k;
                    else
                        for (k = lastgood; k <= j; ++k)
                            lpOrder[done + k] = done + k;
                    lastgood = ++j;
                }
            if (odd(levels[lastgood]))
                for (k = j - 1; k >= lastgood; --k)
                    lpOrder[done + k] = done + j - 1 - k;
            else
575
                for (k = lastgood; k < j; ++k)
576 577
                    lpOrder[done + k] = done + k;
        }
578 579 580

        if (lpGlyphs && doGlyphs)
        {
581 582
            BYTE *runOrder;
            int *visOrder;
583 584
            SCRIPT_ITEM *curItem;

585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
            runOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*runOrder));
            visOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*visOrder));
            if (!runOrder || !visOrder)
            {
                WARN("Out of memory\n");
                HeapFree(GetProcessHeap(), 0, runOrder);
                HeapFree(GetProcessHeap(), 0, visOrder);
                HeapFree(GetProcessHeap(), 0, chartype);
                HeapFree(GetProcessHeap(), 0, levels);
                HeapFree(GetProcessHeap(), 0, pItems);
                HeapFree(GetProcessHeap(), 0, psva);
                HeapFree(GetProcessHeap(), 0, pwLogClust);
                return FALSE;
            }

600 601 602
            for (j = 0; j < nItems; j++)
                runOrder[j] = pItems[j].a.s.uBidiLevel;

603
            ScriptLayout(nItems, runOrder, visOrder, NULL);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620

            for (j = 0; j < nItems; j++)
            {
                int k;
                int cChars,cOutGlyphs;
                curItem = &pItems[visOrder[j]];

                cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;

                res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
                while (res == E_OUTOFMEMORY)
                {
                    cMaxGlyphs *= 2;
                    run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(WORD) * cMaxGlyphs);
                    if (!run_glyphs)
                    {
                        WARN("Out of memory\n");
621 622
                        HeapFree(GetProcessHeap(), 0, runOrder);
                        HeapFree(GetProcessHeap(), 0, visOrder);
623 624 625 626 627 628 629 630 631 632 633 634
                        HeapFree(GetProcessHeap(), 0, chartype);
                        HeapFree(GetProcessHeap(), 0, levels);
                        HeapFree(GetProcessHeap(), 0, pItems);
                        HeapFree(GetProcessHeap(), 0, psva);
                        HeapFree(GetProcessHeap(), 0, pwLogClust);
                        HeapFree(GetProcessHeap(), 0, *lpGlyphs);
                        ScriptFreeCache(&psc);
                        *lpGlyphs = NULL;
                        return FALSE;
                    }
                    res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
                }
635
                if (res)
636
                {
637 638 639 640
                    if (res == USP_E_SCRIPT_NOT_IN_FONT)
                        TRACE("Unable to shape with currently selected font\n");
                    else
                        FIXME("Unable to shape string (%x)\n",res);
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
                    j = nItems;
                    doGlyphs = FALSE;
                    HeapFree(GetProcessHeap(), 0, *lpGlyphs);
                    *lpGlyphs = NULL;
                }
                else
                {
                    if (*lpGlyphs)
                        *lpGlyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(WORD) * (glyph_i + cOutGlyphs));
                   else
                        *lpGlyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(WORD) * (glyph_i + cOutGlyphs));
                    for (k = 0; k < cOutGlyphs; k++)
                        (*lpGlyphs)[glyph_i+k] = run_glyphs[k];
                    glyph_i += cOutGlyphs;
                }
            }
657 658
            HeapFree(GetProcessHeap(), 0, runOrder);
            HeapFree(GetProcessHeap(), 0, visOrder);
659 660
        }

661 662
        done += i;
    }
663 664
    if (cGlyphs)
        *cGlyphs = glyph_i;
665 666

    HeapFree(GetProcessHeap(), 0, chartype);
667 668
    HeapFree(GetProcessHeap(), 0, levels);
    HeapFree(GetProcessHeap(), 0, pItems);
669 670 671 672
    HeapFree(GetProcessHeap(), 0, run_glyphs);
    HeapFree(GetProcessHeap(), 0, pwLogClust);
    HeapFree(GetProcessHeap(), 0, psva);
    ScriptFreeCache(&psc);
673 674
    return TRUE;
}