comctl32/listview: Implement overlay image support.
[wine] / dlls / gdi32 / bidi.c
index 3717137..1d6be53 100644 (file)
@@ -49,6 +49,7 @@
 #include "wingdi.h"
 #include "winnls.h"
 #include "usp10.h"
+#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "gdi_private.h"
 
@@ -105,109 +106,51 @@ enum directions
 
 /* HELPER FUNCTIONS */
 
-/* grep -r ';BN;' data.txt  | grep -v [0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F] | sed -e s@\;.*@@ -e s/^..../0x\&,\ / | xargs echo */
-static const WCHAR BNs[] = {
-    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
-    0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016,
-    0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x007F, 0x0080, 0x0081, 0x0082,
-    0x0083, 0x0084, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
-    0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
-    0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E,
-    0x009F, 0x00AD, 0x070F, 0x200B, 0x200C, 0x200D, 0x2060, 0x2061, 0x2062,
-    0x2063, 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F, 0xFEFF
-};
-
-/* Idem, but with ';R;' instead of ';BN;' */
-static const WCHAR Rs[] = {
-    0x05BE, 0x05C0, 0x05C3, 0x05C6, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4,
-    0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD,
-    0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6,
-    0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4,
-    0x07C0, 0x07C1, 0x07C2, 0x07C3, 0x07C4, 0x07C5, 0x07C6, 0x07C7, 0x07C8,
-    0x07C9, 0x07CA, 0x07CB, 0x07CC, 0x07CD, 0x07CE, 0x07CF, 0x07D0, 0x07D1,
-    0x07D2, 0x07D3, 0x07D4, 0x07D5, 0x07D6, 0x07D7, 0x07D8, 0x07D9, 0x07DA,
-    0x07DB, 0x07DC, 0x07DD, 0x07DE, 0x07DF, 0x07E0, 0x07E1, 0x07E2, 0x07E3,
-    0x07E4, 0x07E5, 0x07E6, 0x07E7, 0x07E8, 0x07E9, 0x07EA, 0x07F4, 0x07F5,
-    0x07FA, 0x200F, 0xFB1D, 0xFB1F, 0xFB20, 0xFB21, 0xFB22, 0xFB23, 0xFB24,
-    0xFB25, 0xFB26, 0xFB27, 0xFB28, 0xFB2A, 0xFB2B, 0xFB2C, 0xFB2D, 0xFB2E,
-    0xFB2F, 0xFB30, 0xFB31, 0xFB32, 0xFB33, 0xFB34, 0xFB35, 0xFB36, 0xFB38,
-    0xFB39, 0xFB3A, 0xFB3B, 0xFB3C, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44,
-    0xFB46, 0xFB47, 0xFB48, 0xFB49, 0xFB4A, 0xFB4B, 0xFB4C, 0xFB4D, 0xFB4E,
-    0xFB4F
-};
-
-/* Convert the incomplete win32 table to some slightly more useful data */
+/* Convert the libwine information to the direction enum */
 static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
 {
-    unsigned i, j;
-    GetStringTypeW(CT_CTYPE2, lpString, uCount, chartype);
+    static const enum directions dir_map[16] =
+    {
+        L,  /* unassigned defaults to L */
+        L,
+        R,
+        EN,
+        ES,
+        ET,
+        AN,
+        CS,
+        B,
+        S,
+        WS,
+        ON,
+        AL,
+        NSM,
+        BN,
+        PDF  /* also LRE, LRO, RLE, RLO */
+    };
+
+    unsigned i;
+
     for (i = 0; i < uCount; ++i)
-        switch (chartype[i])
+    {
+        chartype[i] = dir_map[get_char_typeW(lpString[i]) >> 12];
+        if (chartype[i] == PDF)
         {
-            case C2_LEFTTORIGHT: chartype[i] = L; break;
-            case C2_RIGHTTOLEFT:
-                chartype[i] = AL;
-                for (j = 0; j < sizeof(Rs)/sizeof(WCHAR); ++j)
-                    if (Rs[j] == lpString[i])
-                    {
-                        chartype[i] = R;
-                        break;
-                    }
-                break;
-
-            case C2_EUROPENUMBER: chartype[i] = EN; break;
-            case C2_EUROPESEPARATOR: chartype[i] = ES; break;
-            case C2_EUROPETERMINATOR: chartype[i] = ET; break;
-            case C2_ARABICNUMBER: chartype[i] = AN; break;
-            case C2_COMMONSEPARATOR: chartype[i] = CS; break;
-            case C2_BLOCKSEPARATOR: chartype[i] = B; break;
-            case C2_SEGMENTSEPARATOR: chartype[i] = S; break;
-            case C2_WHITESPACE: chartype[i] = WS; break;
-            case C2_OTHERNEUTRAL:
-                switch (lpString[i])
-                {
-                    case 0x202A: chartype[i] = LRE; break;
-                    case 0x202B: chartype[i] = RLE; break;
-                    case 0x202C: chartype[i] = PDF; break;
-                    case 0x202D: chartype[i] = LRO; break;
-                    case 0x202E: chartype[i] = RLO; break;
-                    default: chartype[i] = ON; break;
-                }
-                break;
-            case C2_NOTAPPLICABLE:
-                chartype[i] = NSM;
-                for (j = 0; j < sizeof(BNs)/sizeof(WCHAR); ++j)
-                    if (BNs[j] == lpString[i])
-                    {
-                        chartype[i] = BN;
-                        break;
-                    }
-                break;
-
-            default:
-                /* According to BiDi spec, unassigned characters default to L */
-                FIXME("Unhandled character type: %04x\n", chartype[i]);
-                chartype[i] = L;
-                break;
+            switch (lpString[i])
+            {
+            case 0x202A: chartype[i] = LRE; break;
+            case 0x202B: chartype[i] = RLE; break;
+            case 0x202C: chartype[i] = PDF; break;
+            case 0x202D: chartype[i] = LRO; break;
+            case 0x202E: chartype[i] = RLO; break;
+            }
         }
-}
-
-/* reverse cch characters */
-static void reverse(LPWSTR psz, int cch)
-{
-    WCHAR chTemp;
-    int ich = 0;
-    for (; ich < --cch; ich++)
-    {
-        chTemp = psz[ich];
-        psz[ich] = psz[cch];
-        psz[cch] = chTemp;
     }
 }
 
 /* Set a run of cval values at locations all prior to, but not including */
 /* iStart, to the new value nval. */
-static void SetDeferredRun(WORD *pval, int cval, int iStart, int nval)
+static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
 {
     int i = iStart - 1;
     for (; i >= iStart - cval; i--)
@@ -255,9 +198,9 @@ static int resolveParagraphs(WORD *types, int cch)
 
     Breaks a paragraph into lines
 
-    Input:  Character count
+    Input:  Array of line break flags
+            Character count
     In/Out: Array of characters
-            Array of line break flags
 
     Returns the count of characters on the first line
 
@@ -266,7 +209,7 @@ static int resolveParagraphs(WORD *types, int cch)
     occurs after the character in pszInput[n]. Breaks before the first
     character are not allowed.
 ------------------------------------------------------------------------*/
-static int resolveLines(LPCWSTR pszInput, BOOL * pbrk, int cch)
+static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
 {
     /* skip characters not of type LS */
     int ich = 0;
@@ -299,10 +242,10 @@ static int resolveLines(LPCWSTR pszInput, BOOL * pbrk, int cch)
           a real implementation, cch and the initial pointer values
           would have to be adjusted.
 ------------------------------------------------------------------------*/
-static void resolveWhitespace(int baselevel, const WORD *pcls, WORD *plevel, int cch)
+static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
 {
     int cchrun = 0;
-    int oldlevel = baselevel;
+    BYTE oldlevel = baselevel;
 
     int ich = 0;
     for (; ich < cch; ich++)
@@ -340,117 +283,31 @@ static void resolveWhitespace(int baselevel, const WORD *pcls, WORD *plevel, int
     SetDeferredRun(plevel, cchrun, ich, baselevel);
 }
 
-
-/*------------------------------------------------------------------------
-    Functions: reorder/reorderLevel
-
-    Recursively reorders the display string
-    "From the highest level down, reverse all characters at that level and
-    higher, down to the lowest odd level"
-
-    Implements rule L2 of the Unicode bidi Algorithm.
-
-    Input: Array of embedding levels
-           Character count
-           Flag enabling reversal (set to false by initial caller)
-
-    In/Out: Text to reorder
-
-    Note: levels may exceed 15 resp. 61 on input.
-
-    Rule L3 - reorder combining marks is not implemented here
-    Rule L4 - glyph mirroring is implemented as a display option below
-
-    Note: this should be applied a line at a time
--------------------------------------------------------------------------*/
-static int reorderLevel(int level, LPWSTR pszText, const WORD* plevel, int cch, BOOL fReverse)
-{
-    int ich = 0;
-
-    /* true as soon as first odd level encountered */
-    fReverse = fReverse || odd(level);
-
-    for (; ich < cch; ich++)
-    {
-        if (plevel[ich] < level)
-        {
-            break;
-        }
-        else if (plevel[ich] > level)
-        {
-            ich += reorderLevel(level + 1, pszText + ich, plevel + ich,
-                cch - ich, fReverse) - 1;
-        }
-    }
-    if (fReverse)
-    {
-        reverse(pszText, ich);
-    }
-    return ich;
-}
-
-static int reorder(int baselevel, LPWSTR pszText, const WORD* plevel, int cch)
-{
-    int ich = 0;
-
-    while (ich < cch)
-    {
-        ich += reorderLevel(baselevel, pszText + ich, plevel + ich,
-            cch - ich, FALSE);
-    }
-    return ich;
-}
-
-/* DISPLAY OPTIONS */
-/*-----------------------------------------------------------------------
-   Function:    mirror
-
-    Crudely implements rule L4 of the Unicode Bidirectional Algorithm
-    Demonstrate mirrored brackets, braces and parens
-
-
-    Input:    Array of levels
-            Count of characters
-
-    In/Out:    Array of characters (should be array of glyph ids)
-
-    Note;
-    A full implementation would need to substitute mirrored glyphs even
-    for characters that are not paired (e.g. integral sign).
------------------------------------------------------------------------*/
-static void mirror(LPWSTR pszInput, const WORD* plevel, int cch)
-{
-    static int warn_once;
-    int i;
-
-    for (i = 0; i < cch; ++i)
-    {
-        if (!odd(plevel[i]))
-            continue;
-        /* This needs the data from http://www.unicode.org/Public/UNIDATA/BidiMirroring.txt */
-        if (!warn_once++)
-            FIXME("stub: mirroring of characters not yet implemented\n");
-        break;
-    }
-}
-
 /*------------------------------------------------------------------------
     Function: BidiLines
 
     Implements the Line-by-Line phases of the Unicode Bidi Algorithm
 
       Input:     Count of characters
-             flag whether to mirror
+                 Array of character directions
 
     Inp/Out: Input text
-             Array of character directions
              Array of levels
 
 ------------------------------------------------------------------------*/
-static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, WORD * pclsLine,
-                      WORD * plevelLine, int cchPara, int fMirror, BOOL * pbrk)
+static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
+                      BYTE * plevelLine, int cchPara, const BOOL * pbrk)
 {
     int cchLine = 0;
+    int done = 0;
+    int *run;
+
+    run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
+    if (!run)
+    {
+        WARN("Out of memory\n");
+        return;
+    }
 
     do
     {
@@ -462,11 +319,11 @@ static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, WORD *
 
         if (pszOutLine)
         {
-            if (fMirror)
-                mirror(pszOutLine, plevelLine, cchLine);
-
+            int i;
             /* reorder each line in place */
-            reorder(baselevel, pszOutLine, plevelLine, cchLine);
+            ScriptLayout(cchLine, plevelLine, NULL, run);
+            for (i = 0; i < cchLine; i++)
+                pszOutLine[done+run[i]] = pszLine[i];
         }
 
         pszLine += cchLine;
@@ -474,26 +331,36 @@ static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, WORD *
         pbrk += pbrk ? cchLine : 0;
         pclsLine += cchLine;
         cchPara -= cchLine;
+        done += cchLine;
 
     } while (cchPara);
+
+    HeapFree(GetProcessHeap(), 0, run);
 }
 
 /*************************************************************
  *    BIDI_Reorder
+ *
+ *     Returns TRUE if reordering was required and done.
  */
 BOOL BIDI_Reorder(
+                HDC hDC,        /*[in] Display DC */
                 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 */
-                UINT *lpOrder /* [out] Logical -> Visual order map */
+                UINT *lpOrder, /* [out] Logical -> Visual order map */
+                WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
+                INT *cGlyphs /* [out] number of glyphs generated */
     )
 {
     WORD *chartype;
-    WORD *levels;
-    unsigned i, done;
+    BYTE *levels;
+    INT i, done;
+    unsigned glyph_i;
+    BOOL is_complex;
 
     int maxItems;
     int nItems;
@@ -501,6 +368,12 @@ BOOL BIDI_Reorder(
     SCRIPT_STATE State;
     SCRIPT_ITEM *pItems;
     HRESULT res;
+    SCRIPT_CACHE psc = NULL;
+    WORD *run_glyphs = NULL;
+    WORD *pwLogClust = NULL;
+    SCRIPT_VISATTR *psva = NULL;
+    DWORD cMaxGlyphs = 0;
+    BOOL  doGlyphs = TRUE;
 
     TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
           debugstr_wn(lpString, uCount), uCount, dwFlags,
@@ -508,6 +381,8 @@ BOOL BIDI_Reorder(
 
     memset(&Control, 0, sizeof(Control));
     memset(&State, 0, sizeof(State));
+    if (lpGlyphs)
+        *lpGlyphs = NULL;
 
     if (!(dwFlags & GCP_REORDER))
     {
@@ -515,7 +390,7 @@ BOOL BIDI_Reorder(
         return FALSE;
     }
 
-    if (uCountOut < uCount)
+    if (lpOutString && uCountOut < uCount)
     {
         FIXME("lpOutString too small\n");
         return FALSE;
@@ -531,11 +406,20 @@ BOOL BIDI_Reorder(
     if (lpOutString)
         memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
 
+    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;
+    }
+
     /* 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;
-    else
+    else if (!is_complex)
     {
         done = 1;
         classify(lpString, chartype, uCount);
@@ -552,11 +436,16 @@ BOOL BIDI_Reorder(
         if (done)
         {
             HeapFree(GetProcessHeap(), 0, chartype);
+            if (lpOrder)
+            {
+                for (i = 0; i < uCount; i++)
+                    lpOrder[i] = i;
+            }
             return TRUE;
         }
     }
 
-    levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
+    levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
     if (!levels)
     {
         WARN("Out of memory\n");
@@ -574,10 +463,46 @@ BOOL BIDI_Reorder(
         return FALSE;
     }
 
+    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;
+        }
+    }
+
     done = 0;
+    glyph_i = 0;
     while (done < uCount)
     {
-        unsigned j;
+        INT j;
         classify(lpString + done, chartype, uCount - done);
         /* limit text to first block */
         i = resolveParagraphs(chartype, uCount - done);
@@ -621,24 +546,30 @@ BOOL BIDI_Reorder(
                 WARN("Out of memory\n");
                 HeapFree(GetProcessHeap(), 0, chartype);
                 HeapFree(GetProcessHeap(), 0, levels);
+                HeapFree(GetProcessHeap(), 0, run_glyphs);
+                HeapFree(GetProcessHeap(), 0, pwLogClust);
+                HeapFree(GetProcessHeap(), 0, psva);
                 return FALSE;
             }
             res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
         }
 
-        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;
-        }
-
-        /* assign directional types again, but for WS, S this time */
-        classify(lpString + done, chartype, i);
+        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;
+            }
 
-        BidiLines(State.uBidiLevel, lpOutString ? lpOutString + done : NULL, lpString + done,
-                    chartype, levels, i, !(dwFlags & GCP_SYMSWAPOFF), 0);
+        if (lpOutString)
+        {
+            /* assign directional types again, but for WS, S this time */
+            classify(lpString + done, chartype, i);
 
+            BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
+                        chartype, levels, i, 0);
+        }
 
         if (lpOrder)
         {
@@ -662,11 +593,81 @@ BOOL BIDI_Reorder(
                 for (k = lastgood; k < j; ++k)
                     lpOrder[done + k] = done + k;
         }
+
+        if (lpGlyphs && doGlyphs)
+        {
+            BYTE runOrder[maxItems];
+            int visOrder[maxItems];
+            SCRIPT_ITEM *curItem;
+
+            for (j = 0; j < nItems; j++)
+                runOrder[j] = pItems[j].a.s.uBidiLevel;
+
+            ScriptLayout(nItems, runOrder, visOrder, NULL);
+
+            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");
+                        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);
+                }
+                if (res)
+                {
+                    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);
+                    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;
+                }
+            }
+        }
+
         done += i;
     }
+    if (cGlyphs)
+        *cGlyphs = glyph_i;
 
     HeapFree(GetProcessHeap(), 0, chartype);
     HeapFree(GetProcessHeap(), 0, levels);
     HeapFree(GetProcessHeap(), 0, pItems);
+    HeapFree(GetProcessHeap(), 0, run_glyphs);
+    HeapFree(GetProcessHeap(), 0, pwLogClust);
+    HeapFree(GetProcessHeap(), 0, psva);
+    ScriptFreeCache(&psc);
     return TRUE;
 }