mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 11:35:58 +00:00
a4a59ad413
Introduce BiDi (bi-directional text) support for ExtTextOut and GetCharacterPlacement, using Wine's GDI BIDI_Reorder function. Solves the main issue with CORE-7003. To be compatible with Win2k3+, introduce the "Language Pack" (LPK) dll. - All the bidi code is removed from gdi32 and replaced by calls to LPK. Gdi32 uses dynamic linking to lpk.dll. In case of linking failure no bidi processing will be available. - Implemented LpkGetCharacterPlacement. - Implement LpkExtTextOut. - Add a demo test program to show how the apis should function. - Added all the remaining code, added special case for lpDx calculation if also GCP_GLYPHSHAPE flag was called. Applications that call GCP that use GCP_GLYPHSHAPE flags should also use the GCP_REORDER flag. (As written in https://msdn.microsoft.com/en-us/library/windows/desktop/dd144860(v=vs.85).aspx ) - Add ETO_RTLREADING flag handling. Imported the ETO_RTLREADING flag handling from wine, which changes the string part order (runs). A RRR1LLLRRR2 string without will show as RRR1LLLRRR2 without it, with it RRR2LLLRRR1.
688 lines
22 KiB
C
688 lines
22 KiB
C
/*
|
|
* GDI BiDirectional handling
|
|
*
|
|
* Copyright 2003 Shachar Shemesh
|
|
* Copyright 2007 Maarten Lankhorst
|
|
* Copyright 2010 CodeWeavers, Aric Stewart
|
|
*
|
|
*
|
|
* 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
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "ros_lpk.h"
|
|
#include "wine/unicode.h"
|
|
#include "wine/debug.h"
|
|
//#include "config.h"
|
|
//#include "gdi_private.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(bidi);
|
|
|
|
/* HELPER FUNCTIONS AND DECLARATIONS */
|
|
|
|
#define odd(x) ((x) & 1)
|
|
|
|
/*------------------------------------------------------------------------
|
|
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
|
|
same way as the table 3-7, since the numberic values for the types
|
|
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,
|
|
|
|
/* resolved types, also resolved directions */
|
|
N = ON, /* alias, where ON, WS and S are treated the same */
|
|
};
|
|
|
|
/* HELPER FUNCTIONS */
|
|
|
|
/* Convert the libwine information to the direction enum */
|
|
static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
|
|
{
|
|
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)
|
|
{
|
|
chartype[i] = dir_map[get_char_typeW(lpString[i]) >> 12];
|
|
if (chartype[i] == PDF)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set a run of cval values at locations all prior to, but not including */
|
|
/* iStart, to the new value nval. */
|
|
static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
|
|
{
|
|
int i = iStart - 1;
|
|
for (; i >= iStart - cval; i--)
|
|
{
|
|
pval[i] = nval;
|
|
}
|
|
}
|
|
|
|
/* 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
|
|
|
|
Note: This is a very simplistic function. In effect it restricts
|
|
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;
|
|
}
|
|
|
|
/* REORDER */
|
|
/*------------------------------------------------------------------------
|
|
Function: resolveLines
|
|
|
|
Breaks a paragraph into lines
|
|
|
|
Input: Array of line break flags
|
|
Character count
|
|
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.
|
|
------------------------------------------------------------------------*/
|
|
static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
|
|
{
|
|
/* 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.
|
|
------------------------------------------------------------------------*/
|
|
static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
|
|
{
|
|
int cchrun = 0;
|
|
BYTE oldlevel = baselevel;
|
|
|
|
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:
|
|
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
|
|
Array of character directions
|
|
|
|
Inp/Out: Input text
|
|
Array of levels
|
|
|
|
------------------------------------------------------------------------*/
|
|
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
|
|
{
|
|
/* break lines at LS */
|
|
cchLine = resolveLines(pszLine, pbrk, cchPara);
|
|
|
|
/* resolve whitespace */
|
|
resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);
|
|
|
|
if (pszOutLine)
|
|
{
|
|
int i;
|
|
/* reorder each line in place */
|
|
ScriptLayout(cchLine, plevelLine, NULL, run);
|
|
for (i = 0; i < cchLine; i++)
|
|
pszOutLine[done+run[i]] = pszLine[i];
|
|
}
|
|
|
|
pszLine += cchLine;
|
|
plevelLine += cchLine;
|
|
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 */
|
|
WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
|
|
INT *cGlyphs /* [out] number of glyphs generated */
|
|
)
|
|
{
|
|
WORD *chartype;
|
|
BYTE *levels;
|
|
INT i, done;
|
|
unsigned glyph_i;
|
|
BOOL is_complex;
|
|
|
|
int maxItems;
|
|
int nItems;
|
|
SCRIPT_CONTROL Control;
|
|
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,
|
|
lpOutString, lpOrder);
|
|
|
|
memset(&Control, 0, sizeof(Control));
|
|
memset(&State, 0, sizeof(State));
|
|
if (lpGlyphs)
|
|
*lpGlyphs = NULL;
|
|
|
|
if (!(dwFlags & GCP_REORDER))
|
|
{
|
|
FIXME("Asked to reorder without reorder flag set\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (lpOutString && uCountOut < uCount)
|
|
{
|
|
FIXME("lpOutString too small\n");
|
|
return FALSE;
|
|
}
|
|
|
|
chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
|
|
if (!chartype)
|
|
{
|
|
WARN("Out of memory\n");
|
|
return FALSE;
|
|
}
|
|
|
|
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 if (!is_complex)
|
|
{
|
|
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);
|
|
if (lpOrder)
|
|
{
|
|
for (i = 0; i < uCount; i++)
|
|
lpOrder[i] = i;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
INT j;
|
|
classify(lpString + done, chartype, uCount - done);
|
|
/* limit text to first block */
|
|
i = resolveParagraphs(chartype, uCount - done);
|
|
for (j = 0; j < i; ++j)
|
|
switch(chartype[j])
|
|
{
|
|
case B:
|
|
case S:
|
|
case WS:
|
|
case ON: chartype[j] = N;
|
|
default: continue;
|
|
}
|
|
|
|
if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL)
|
|
State.uBidiLevel = 1;
|
|
else if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_LTR)
|
|
State.uBidiLevel = 0;
|
|
|
|
if (dwWineGCP_Flags & WINE_GCPW_LOOSE_MASK)
|
|
{
|
|
for (j = 0; j < i; ++j)
|
|
if (chartype[j] == L)
|
|
{
|
|
State.uBidiLevel = 0;
|
|
break;
|
|
}
|
|
else if (chartype[j] == R || chartype[j] == AL)
|
|
{
|
|
State.uBidiLevel = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int k, lastgood;
|
|
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
|
|
for (k = lastgood; k < j; ++k)
|
|
lpOrder[done + k] = done + k;
|
|
}
|
|
|
|
if (lpGlyphs && doGlyphs)
|
|
{
|
|
BYTE *runOrder;
|
|
int *visOrder;
|
|
SCRIPT_ITEM *curItem;
|
|
|
|
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;
|
|
}
|
|
|
|
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, 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);
|
|
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;
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, runOrder);
|
|
HeapFree(GetProcessHeap(), 0, visOrder);
|
|
}
|
|
|
|
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;
|
|
}
|