Uniscribe improvements

* * richardvoigt at gmail.com
Tue Jan 2 12:42:25 CST 2007


StringAnalysis should have a cache object, not save the HDC directly.
One of the important things about the cache, which wine currently
doesn't do, is that the caller is free to select a different font into
the HDC after calling ScriptStringAnalyse and Uniscribe is supposed to
use the right one.

I had also implemented ScriptStringOut, but while messing with
Direct3D crashes, someone beat me to the patch.  I had been calling
ScriptTextOut for each run returned by ScriptItemize, I still think
that is more correct than combining everything into a single string.
But I used the accepted version of ScriptStringOut with just a couple
additions.
-------------- next part --------------
From 233c6a2bc519c15b0a383ce3d227cd69d6206c98 Mon Sep 17 00:00:00 2001
From: Richard Voigt richardvoigt at gmail.com <ben at gondor.lan>
Date: Tue, 2 Jan 2007 12:40:59 -0600
Subject: In Uniscribe, save cache and not HDC directly, also cleanup ScriptItemize and fix buffer overflow caused by bad info on MSDN

---
 dlls/usp10/usp10.c |  180 +++++++++++++++++++++++++++------------------------
 1 files changed, 95 insertions(+), 85 deletions(-)

diff --git a/dlls/usp10/usp10.c b/dlls/usp10/usp10.c
index 0334e93..02ec3be 100644
--- a/dlls/usp10/usp10.c
+++ b/dlls/usp10/usp10.c
@@ -159,7 +159,7 @@ typedef struct {
 
 typedef struct {
     BOOL invalid;
-    HDC hdc;
+    SCRIPT_CACHE cache;
     int cItems;
     int cMaxGlyphs;
     SCRIPT_ITEM* pItem;
@@ -402,8 +402,12 @@ HRESULT WINAPI ScriptItemize(const WCHAR
 #define Script_Latin   1
 #define Script_Numeric 5
 
-    int   cnt = 0, index = 0;
-    int   New_Script = SCRIPT_UNDEFINED;
+    int   cnt = 0;
+    int   New_Script;
+    int   PrevScript = -1;
+    SCRIPT_ITEM *pItem = pItems;
+    SCRIPT_ITEM *pEndItem = pItems + cMaxItems; /* points to required extra "terminal item" */
+    WCHAR wc;
 
     TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems, 
           psControl, psState, pItems, pcItems);
@@ -411,75 +415,50 @@ HRESULT WINAPI ScriptItemize(const WCHAR
     if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
         return E_INVALIDARG;
 
-    pItems[index].iCharPos = 0;
-    memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
-
-    if  (pwcInChars[cnt] >= Numeric_start && pwcInChars[cnt] <= Numeric_stop)
-        pItems[index].a.eScript = Script_Numeric;
-    else
-    if  (pwcInChars[cnt] >= Arabic_start && pwcInChars[cnt] <= Arabic_stop)
-        pItems[index].a.eScript = Script_Arabic;
-    else
-    if  (pwcInChars[cnt] >= Latin_start && pwcInChars[cnt] <= Latin_stop)
-        pItems[index].a.eScript = Script_Latin;
-
-    if  (pItems[index].a.eScript  == Script_Arabic)
-        pItems[index].a.s.uBidiLevel = 1;
+    wc = *pwcInChars;
+    goto DetermineScript;
+
+    do {
+        TRACE("New_Script=%d, cnt=%d\n", New_Script, cnt);
+        pItem->iCharPos = cnt;
+        memset(&pItem->a, 0, sizeof(SCRIPT_ANALYSIS));
+        pItem->a.eScript = PrevScript = New_Script;
+        if  (New_Script  == Script_Arabic)
+            pItem->a.s.uBidiLevel = 1;
+        pItem++;
+
+    	do {
+            /* From MSDN, "The function always adds a terminal item to the item analysis array..." */
+            if (++cnt >= cInChars) {
+        	pItem->iCharPos = cnt;
+        	memset(&pItem->a, 0, sizeof(SCRIPT_ANALYSIS));
+        	*pcItems = pItem - pItems;
+
+        	return S_OK;
+            }
 
-    TRACE("New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
-          New_Script, pItems[index].a.eScript, index, cnt,
-          pItems[index].iCharPos = cnt);
+            wc = pwcInChars[cnt];
+            if (wc == Numeric_space) {
+        	/* space character can't change script */;
+        	continue;
+            }
 
-    for (cnt=0; cnt < cInChars; cnt++)
-    {
-        if  ((pwcInChars[cnt] >= Numeric_start && pwcInChars[cnt] <= Numeric_stop)
-             || (New_Script == Script_Numeric && pwcInChars[cnt] == Numeric_space))
+    DetermineScript:
+            if  (wc >= Numeric_start && wc <= Numeric_stop)
             New_Script = Script_Numeric;
         else
-        if  ((pwcInChars[cnt] >= Arabic_start && pwcInChars[cnt] <= Arabic_stop)
-             || (New_Script == Script_Arabic && pwcInChars[cnt] == Numeric_space))
+            if  (wc >= Arabic_start && wc <= Arabic_stop)
             New_Script = Script_Arabic;
         else
-        if  ((WCHAR) pwcInChars[cnt] >= Latin_start && (WCHAR) pwcInChars[cnt] <= Latin_stop)
+            if  (wc >= Latin_start && wc <= Latin_stop)
             New_Script = Script_Latin;
         else
             New_Script = SCRIPT_UNDEFINED;
-        
-        if  (New_Script != pItems[index].a.eScript)
-        {
-            TRACE("New_Script=%d, eScript=%d ", New_Script, pItems[index].a.eScript);
-            index++;
-            if  (index+1 > cMaxItems)
-                return E_OUTOFMEMORY;
-
-            pItems[index].iCharPos = cnt;
-            memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
-
-            if  (New_Script == Script_Arabic)
-                pItems[index].a.s.uBidiLevel = 1;
-
-            pItems[index].a.eScript = New_Script;
-            if  (New_Script == Script_Arabic)
-                pItems[index].a.s.uBidiLevel = 1;
-
-            TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos = cnt);
-        }
-    }
-
-    /* While not strictly necessary according to the spec, make sure the n+1
-     * item is set up to prevent random behaviour if the caller erroneously
-     * checks the n+1 structure                                              */
-    memset(&pItems[index+1].a, 0, sizeof(SCRIPT_ANALYSIS));
-
-    TRACE("index=%d cnt=%d iCharPos=%d\n", index+1, cnt, pItems[index+1].iCharPos = cnt);
+        } while (New_Script == PrevScript);
+        /* script changed, advance pItem, if space permits */
+    } while (pItem < pEndItem);
 
-    /*  Set one SCRIPT_STATE item being returned  */
-    *pcItems = index + 1;
-
-    /*  Set SCRIPT_ITEM                                     */
-    pItems[index+1].iCharPos = cnt;       /* the last + 1 item
-                                             contains the ptr to the lastchar */
-    return S_OK;
+    return E_OUTOFMEMORY;
 }
 
 /***********************************************************************
@@ -504,7 +483,7 @@ HRESULT WINAPI ScriptStringAnalyse(HDC h
     StringAnalysis* analysis;
     int numItemizedItems;
     int i;
-    SCRIPT_CACHE* sc = 0;
+    SCRIPT_CACHE* sc = NULL;
 
     TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
       hdc, pString, cString, cGlyphs, iCharset, dwFlags,
@@ -519,21 +498,30 @@ HRESULT WINAPI ScriptStringAnalyse(HDC h
     analysis = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                        sizeof(StringAnalysis));
 
-    analysis->hdc = hdc;
+    sc = &analysis->cache;
+    *sc = NULL;
+    get_script_cache(hdc, sc);
+    if (analysis->cache == NULL) {
+	TRACE("Couldn't make cache object\n");
+    }
     numItemizedItems = 255;
     analysis->pItem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
-                              numItemizedItems*sizeof(SCRIPT_ITEM)+1);
-
-    hr = ScriptItemize(pString, cString, numItemizedItems, psControl,
-                     psState, analysis->pItem, &analysis->numItems);
+                              (numItemizedItems+1)*sizeof(SCRIPT_ITEM));
+    if (analysis->pItem == NULL) {
+        HeapFree(GetProcessHeap(), 0, analysis);
+    	return E_OUTOFMEMORY;
+    }
 
-    while(hr == E_OUTOFMEMORY)
+    while(E_OUTOFMEMORY == ScriptItemize(pString, cString, numItemizedItems, psControl,
+                                         psState, analysis->pItem, &analysis->numItems))
     {
         numItemizedItems *= 2;
-        HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, analysis->pItem,
-                    numItemizedItems*sizeof(SCRIPT_ITEM)+1);
-        hr = ScriptItemize(pString, cString, numItemizedItems, psControl,
-                           psState, analysis->pItem, &analysis->numItems);
+        if (NULL == HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, analysis->pItem,
+                    (numItemizedItems+1)*sizeof(SCRIPT_ITEM))) {
+	    HeapFree(GetProcessHeap(), 0, analysis->pItem);
+	    HeapFree(GetProcessHeap(), 0, analysis);
+	    return E_OUTOFMEMORY;
+	}
     }
 
     if ((analysis->logattrs = HeapAlloc(GetProcessHeap(), 0, sizeof(SCRIPT_LOGATTR) * cString)))
@@ -541,7 +529,6 @@ HRESULT WINAPI ScriptStringAnalyse(HDC h
 
     analysis->glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                  sizeof(StringGlyphs)*analysis->numItems);
-    sc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SCRIPT_CACHE));
 
     for(i=0; i<analysis->numItems; i++)
     {
@@ -572,10 +559,10 @@ HRESULT WINAPI ScriptStringAnalyse(HDC h
         analysis->glyphs[i].abc = abc;
     }
 
-    HeapFree(GetProcessHeap(), 0, sc);
-
     *pssa = analysis;
 
+    TRACE("ScriptStringAnalyse success, StringAnalysis struct at %p\n", analysis);
+
     return S_OK;
 }
 
@@ -612,14 +599,32 @@ HRESULT WINAPI ScriptStringOut(SCRIPT_ST
     StringAnalysis *analysis;
     WORD *glyphs;
     int   item, cnt, x;
+    HDC hdc;
     HRESULT hr;
     SCRIPT_CACHE sc = 0;
 
-    TRACE("(%p,%d,%d,0x%1x,%p,%d,%d,%d)\n",
-         ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
+    if (iMinSel < iMaxSel) {
+        FIXME("(%p,%d,%d,0x%1x,%p,%d,%d,%d): semi-stub, selection highlight not implemented\n",
+              ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
+    }
+    else
+        TRACE("(%p,%d,%d,0x%1x,%p,%d,%d,%d)\n",
+                ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
 
     analysis = ssa;                             /* map ptr to string_analysis struct */
 
+    hdc = ((ScriptCache*)analysis->cache)->hdc;
+    if (uOptions & ETO_OPAQUE) {
+	    /* if ETO_OPAQUE is set, fill rectangle with background once beforehand, then draw text transparently */
+	    /* this allows runs of text to overlap properly */
+	    WCHAR c;
+	    ExtTextOutW(hdc, iX, iY, prc, ETO_OPAQUE, &c, 0, NULL);
+	    uOptions &= ~ETO_OPAQUE;
+    }
+
+    /* TODO: handle hard line breaks and word wrap */
+    /* TODO: handle highlights */
+
     /*
      * Get storage for the output buffer for the consolidated strings
      */
@@ -656,7 +661,7 @@ HRESULT WINAPI ScriptStringOut(SCRIPT_ST
         cnt += analysis->glyphs[item].numGlyphs; /* point to the end of the copied text */
     }
 
-    hr = ScriptTextOut(analysis->hdc, &sc, iX, iY, uOptions, prc, &analysis->pItem->a,
+    hr = ScriptTextOut(hdc, &sc, iX, iY, uOptions, prc, &analysis->pItem->a,
                        NULL, 0, glyphs, cnt, analysis->glyphs->piAdvance, NULL,
                        analysis->glyphs->pGoffset);
     TRACE("ScriptTextOut hr=%08x\n", hr);
@@ -807,6 +812,7 @@ HRESULT WINAPI ScriptStringFree(SCRIPT_S
     HeapFree(GetProcessHeap(), 0, analysis->pItem);
     HeapFree(GetProcessHeap(), 0, analysis->logattrs);
     HeapFree(GetProcessHeap(), 0, analysis->sz);
+    ScriptFreeCache(&analysis->cache);
     HeapFree(GetProcessHeap(), 0, analysis);
 
     if(invalid)
@@ -1140,9 +1146,11 @@ HRESULT WINAPI ScriptShape(HDC hdc, SCRI
     if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
     if ((hr = get_script_cache(hdc, psc))) return hr;
 
-    TRACE("Before: ");
-    for (cnt = 0; cnt < cChars; cnt++)
-         TRACE("%4x",pwcChars[cnt]);
+    TRACE("Before:");
+    for( cnt = 0; cnt < cChars; cnt++ ) {
+	if ((cChars > 16) && ((cnt & 0xf) == 0)) TRACE("\n");
+        TRACE("  %04x",pwcChars[cnt]);
+    }
     TRACE("\n");
 
     if  (!psa->fNoGlyphIndex) {                                         /* Glyph translate */
@@ -1151,15 +1159,17 @@ HRESULT WINAPI ScriptShape(HDC hdc, SCRI
 
         TRACE("After:  ");
         for (cnt = 0; cnt < cChars; cnt++) {
-             TRACE("%04x",pwOutGlyphs[cnt]);
+	     if ((cChars > 16) && ((cnt & 0xf) == 0)) TRACE("\n");
+             TRACE("  %04x",pwOutGlyphs[cnt]);
         }
         TRACE("\n");
     }
     else {
         TRACE("After:  ");
         for (cnt = 0; cnt < cChars; cnt++) {                           /* no translate so set up */
+	     if ((cChars > 16) && ((cnt & 0xf) == 0)) TRACE("\n");
              pwOutGlyphs[cnt] = pwcChars[cnt];                         /* copy in to out and     */
-             TRACE("%04x",pwOutGlyphs[cnt]);
+             TRACE("  %04x",pwOutGlyphs[cnt]);
         }
        TRACE("\n");
     }
@@ -1483,7 +1493,7 @@ const SIZE * WINAPI ScriptString_pSize(S
          * appropriate place so that we can just pass cached
          * values here.
          */
-        if (!GetTextMetricsW(analysis->hdc, &metric))
+        if (!GetTextMetricsW(((ScriptCache*)analysis->cache)->hdc, &metric))
         {
             HeapFree(GetProcessHeap(), 0, analysis->sz);
             analysis->sz = NULL;
-- 
1.4.4.1


More information about the wine-patches mailing list