usp10 3/3: Implement ScriptStringXtoCP and ScriptStringCPtoX

Jeff L lats at yless4u.com.au
Wed Oct 11 16:07:04 CDT 2006


ScriptStringXtoCP and ScriptStringCPtoX use the results of
ScriptStringAnalysis to determine X positions and Character Posistions
within a string.  A good example of how this works is in
scriptstring.exe from http://www.catch22.net/tuts/editor12.asp which
uses the functions extensively.  The tests that are part of this patch
use the results of one function as input to the other as the metrics are
not consistent between wine and Windows.

All documentation has been written by me.

Jeff Latimer

Changelog: Implement ScriptStringXtoCP and ScriptStringCPtoX

-------------- next part --------------
>From 77a978f59961fac925aad76f53f73785fd22c1a8 Mon Sep 17 00:00:00 2001
From: Jeff Latimer <lats at yless4u.com.au>
Date: Thu, 12 Oct 2006 00:27:09 +1000
Subject: [PATCH] Implement ScriptStringXtoCP and ScriptStringCPtoX
---
 dlls/usp10/tests/usp10.c |  105 ++++++++++++++++++++++++++
 dlls/usp10/usp10.c       |  184 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 283 insertions(+), 6 deletions(-)

diff --git a/dlls/usp10/tests/usp10.c b/dlls/usp10/tests/usp10.c
index a97f45a..2f5dab2 100644
--- a/dlls/usp10/tests/usp10.c
+++ b/dlls/usp10/tests/usp10.c
@@ -680,6 +680,108 @@ static void test_ScriptString(void)
     }
 }
 
+void test_ScriptStringXtoCP_CPtoX(HDC hdc)
+{
+/*****************************************************************************************
+ *
+ * This test is for the ScriptStringXtoCP and ScriptStringXtoCP functions.  Due to the
+ * nature of the fonts between Windows and Wine, the test is implemented by generating
+ * values using one one function then checking the output of the second.  In this way
+ * the validity of the functions is established using Windows as a base and confirming
+ * similar behaviour in wine.
+ */
+
+    HRESULT         hr;
+    WCHAR           teststr1[] = {'T', 'e', 's', 't', 'e', '1', '2', ' ', 'a', '\0'};
+    void            *String = (WCHAR *) &teststr1;      /* ScriptStringAnalysis needs void */
+    int             String_len = (sizeof(teststr1)/sizeof(WCHAR))-1;
+    int             Glyphs = String_len * 2 + 16;       /* size of buffer as recommended  */
+    int             Charset = -1;                       /* unicode                        */
+    DWORD           Flags = SSA_GLYPHS;
+    int             ReqWidth = 100;
+    SCRIPT_CONTROL  Control;
+    SCRIPT_STATE    State;
+    SCRIPT_TABDEF   Tabdef;
+    const BYTE      InClass = 0;
+    SCRIPT_STRING_ANALYSIS ssa = NULL;
+
+    int             Ch;                                  /* Character position in string */
+    int             iTrailing;
+    int             Cp;                                  /* Character position in string */
+    int             X;
+    BOOL            fTrailing;
+
+    LOGFONTA        lf;
+    HFONT           zfont;
+
+    lstrcpyA(lf.lfFaceName, "Symbol");
+    lf.lfHeight = 10;
+    lf.lfCharSet = 0;
+    lf.lfItalic = 0;
+    lf.lfEscapement = 0;
+    lf.lfOrientation = 0;
+    lf.lfUnderline = 0;
+    lf.lfStrikeOut = 0;
+    lf.lfWeight = 400;
+    lf.lfWidth = 0;
+    lf.lfPitchAndFamily = 0;
+
+    zfont = (HFONT) SelectObject(hdc, CreateFontIndirectA(&lf));
+
+    /* test with hdc, this should be a valid test  */
+    hr = ScriptStringAnalyse( hdc, String, String_len, Glyphs, Charset, Flags,
+                              ReqWidth, &Control, &State, NULL, &Tabdef,
+                              &InClass, &ssa);
+    ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08lx\n", hr);
+    ok(ssa != NULL, "ScriptStringAnalyse ssa should not be NULL\n");
+    if  (hr == 0)
+    {
+        for (Cp=0; Cp < String_len; Cp++)
+        {
+            fTrailing = FALSE;
+            hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
+            ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08lx\n", hr);
+            hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
+            ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08lx\n", hr);
+            ok(Cp == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Ch, Cp, X);
+            ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", 
+                                  iTrailing, X);
+            fTrailing = TRUE;
+            hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
+            ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08lx\n", hr);
+            hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
+            ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08lx\n", hr);
+            ok(Cp + 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Ch, Cp + 1, X);
+            ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", 
+                                   iTrailing, X);
+        }
+
+        hr = ScriptStringFree(&ssa);
+        ok(hr == S_OK, "ScriptStringFree should return S_OK not %08lx\n", hr);
+
+        /* Test to see that exceeding the number of chars returnS E_INVALIDARG */
+        hr = ScriptStringAnalyse( hdc, String, String_len, Glyphs, Charset, Flags,
+                                  ReqWidth, &Control, &State, NULL, &Tabdef,
+                                  &InClass, &ssa);
+        ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08lx\n", hr);
+
+        fTrailing = FALSE;
+        Cp = String_len + 1; 
+        hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
+        ok(hr == E_INVALIDARG, "ScriptStringCPtoX should return E_INVALIDARG not %08lx\n", hr);
+
+        hr = ScriptStringFree(&ssa);
+        /*
+         * ScriptStringCPtoX should free ssa but yet implemented , hence ScriptStringFree
+         * returns 0 rather than E_INVALIDARG
+         */
+        todo_wine
+        {
+            ok(hr == E_INVALIDARG, "ScriptStringFree should return E_INVALIDARG not %08lx\n", hr);
+        }
+    }   
+}
+
 void test_ScriptCacheGetHeight(HDC hdc)
 {
     HRESULT hr;
@@ -1058,8 +1160,9 @@ START_TEST(usp10)
     test_ScriptTextOut();
     test_ScriptXtoX();
     test_ScriptString();
-    test_ScriptLayout();
+    test_ScriptStringXtoCP_CPtoX(hdc);
 
+    test_ScriptLayout();
     test_digit_substitution();
 
     ReleaseDC(hwnd, hdc);
diff --git a/dlls/usp10/usp10.c b/dlls/usp10/usp10.c
index f50b226..aac9072 100644
--- a/dlls/usp10/usp10.c
+++ b/dlls/usp10/usp10.c
@@ -760,9 +760,84 @@ HRESULT WINAPI ScriptStringOut(SCRIPT_ST
  *
  */
 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
+/******************************************************************************
+ *
+ * This function takes the output of ScriptStringAnalyse and the charater position
+ * in hte string and calculates an X display position.
+ *
+ * Parameters:
+ *  ssa       [I] buffer to hold the analysed string components
+ *  icp       [I] character position in the string
+ *  fTrailing [I] pointion at the leading or training edge
+ *  pX        [O] output position 
+ */
+
 {
-    FIXME("(%p), %d, %d, (%p): stub\n", ssa, icp, fTrailing, pX);
-    *pX = 0;                             /* Set a reasonable value */
+    string_analysis *analysis;
+    int   item, cnt = 0;
+    int   posX = 0;                                   /* Advance width for the glyphs */
+    float max_PosX = 0;                               /* Total advance width       */
+    int   max_Glyphs = 0;                             /* Total number of glyphs    */
+    int   Glyphs = 0;                                 /* glyph posn in the string  */
+
+    TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
+
+    if  (!ssa) {
+        return E_INVALIDARG;
+    }
+    analysis = ssa;
+
+    print_ssa(ssa);
+
+    if  (icp < 0)                                      /* Before first char         */
+    {
+        *pX = 0;                                       /* Point to first char's     */
+        return S_OK;                                   /* leading edge              */
+    }
+
+    /*
+     * Add up the total Advance width and the total number of glyphs
+     */
+    for (item = 0; item < analysis->num_Items; item++)
+    {                                                  /* total piAdvance           */
+        for (cnt=0; cnt < analysis->script_blk[item].Glyphs; cnt++)
+            max_PosX += analysis->script_blk[item].Advance[cnt];
+        max_Glyphs += analysis->script_blk[item].Glyphs;
+    }
+
+    /*
+     * This ssa could be different to the one that was used to allocate the storage
+     * initially.  Ideally we should free the storage but we don't know if the point
+     * is still valid.  To fix this we need to use an object list to record valid 
+     * storage objects so that an accurate check of allocated storage can be done.
+     */
+    if  (icp >= max_Glyphs)                            /* Greater char count than   */
+    {                                                  /* is in the ssa structure.  */
+        FIXME("Should call ScriptStringFree but can't be sure of the ssa\n");        
+        return E_INVALIDARG;                           /* addrees of the orig ssa   */
+    }
+
+    /*
+     * step through the script items and count the Advance widths till 
+     * the X position is reached
+     */
+    for (item = 0; item < analysis->num_Items && Glyphs <= icp; item++)
+    {
+        for (cnt=0; cnt < analysis->script_blk[item].Glyphs  
+                    &&  Glyphs <= icp; cnt++, Glyphs++)
+        {
+            posX += analysis->script_blk[item].Advance[cnt];
+            TRACE("cnt=%d posX=%d iGlyphs=%d\n", cnt, posX, Glyphs);
+        }
+    }
+
+    if  (!fTrailing)                                   /* point to the leading edge */
+        posX -= analysis->script_blk[item-1].Advance[cnt-1];
+
+    *pX = posX;                                        /* Return the cursor posn */
+
+    TRACE("cnt=%d *pX=%d\n", cnt, *pX);
+
     return S_OK;
 }
 
@@ -771,10 +846,109 @@ HRESULT WINAPI ScriptStringCPtoX(SCRIPT_
  *
  */
 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing) 
+/******************************************************************************
+ *
+ * This function takes the output of ScriptStringAnalyse and the character position
+ * in the string and calculates an X display position.  Leading and trailing edges 
+ * need to indicated.
+ *
+ * Parameters:
+ *  ssa        [I] buffer to hold the analysed string components
+ *  iX         [I] the X position of the charater in the string
+ *  piCh       [O] the charater corresponding to the X position
+ *  piTrailing [O] the X position is in the first or second half of the character
+ */
+
 {
-    FIXME("(%p), %d, (%p), (%p): stub\n", ssa, iX, piCh, piTrailing);
-    *piCh = 0;                          /* Set a reasonable value */
-    *piTrailing = 0;
+    string_analysis *analysis;
+    int   item, cnt = 0;
+    int   posX = 0;
+    int   num_Chars = 0;
+    float max_PosX = 0;
+
+    TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
+
+    if  (!ssa) {
+        return E_INVALIDARG;
+    }
+
+    analysis = ssa;
+
+    if  (iX < 0)                                    /* iX is before start of run */
+    {
+        *piCh = -1;
+        *piTrailing = TRUE;
+        TRACE("iX=%d, *piCh=%d\n", iX, *piCh);
+        return S_OK;
+    }
+
+    if  (iX == 0)                                   /* iX is at the start, do no more */
+    {
+        *piCh = 0;
+        *piTrailing = FALSE;
+        TRACE("iX=%d, *piCh=%d\n", iX, *piCh);
+        return S_OK;
+    } 
+
+    /*
+     * Total the Advance widths and the Total number of characters so that we can
+     * determine if overflow has occured
+     */
+    for (item = 0; item < analysis->num_Items; item++)
+    {        for (cnt=0; cnt < analysis->script_blk[item].Glyphs; cnt++)
+            max_PosX +=  analysis->script_blk[item].Advance[cnt];
+
+        num_Chars += analysis->s_Item[item+1].iCharPos /* add number of chars in */
+                  - analysis->s_Item[item].iCharPos;   /* item to total          */
+     }
+
+    if  (iX >= max_PosX)                            /* iX too large?              */
+    {                                               /* yes, overflow             */
+        *piCh = num_Chars;                          /* return last char          */
+        *piTrailing = FALSE;                        /* say not trailing edge     */
+
+        TRACE("iX=%d >= max_PosX=%f, num_Chars=%d\n", iX, max_PosX, num_Chars);
+
+        return S_OK;
+    }        
+
+    /*
+     * Step through the script items totalling the Advance widths in posX for the 
+     * glyphs and stop whe the total Advance width exceeds the input X value in iX.
+     */
+    num_Chars = 0;
+    for (item = 0; item < analysis->num_Items  && posX <= iX; item++)
+    {                                               /* total piAdvance           */
+        for (cnt=0; cnt < analysis->script_blk[item].Glyphs
+                    && posX <= iX; cnt++)
+        {
+            TRACE("cnt=%d,  Glyphs=%d, Advance=%d\n" , cnt, 
+                  analysis->script_blk[item].Glyphs,
+                  analysis->script_blk[item].Advance[cnt]);
+            posX +=  analysis->script_blk[item].Advance[cnt];
+        }
+        if  (posX <= iX)
+        {   /* keep track of the characters in script_items before this one.  cnt is the * 
+             * index to this script_item                                                 */
+            num_Chars += analysis->s_Item[item+1].iCharPos - analysis->s_Item[item].iCharPos;
+
+            TRACE("item=%d,  num_Chars=%d, Str_len=%d\n" , item, num_Chars,
+                  analysis->s_Item[item+1].iCharPos - analysis->s_Item[item].iCharPos);
+        } 
+    }
+
+    /*
+     * Is iX in the first half of the character or the last half of it
+     * indicate this by setting the piTrailing flag 
+     */
+    if  (posX - iX > analysis->script_blk[item-1].Advance[cnt-1]/2)
+        *piTrailing = FALSE;
+    else
+        *piTrailing = TRUE;                         /* yep we are over half way  */
+    
+    *piCh = num_Chars + cnt -1;                     /* Return character position */
+    TRACE("*piCH=%d posX=%d piTrailing=%d (posX-iX)=%d\n", *piCh, posX, *piTrailing,
+          (posX-iX));
     return S_OK;
 }
 
-- 
1.4.1



More information about the wine-patches mailing list