2 * GDI BiDirectional handling
4 * Copyright 2003 Shachar Shemesh
5 * Copyright 2007 Maarten Lankhorst
6 * Copyright 2010 CodeWeavers, Aric Stewart
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * 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
22 * Code derived from the modified reference implementation
23 * that was found in revision 17 of http://unicode.org/reports/tr9/
24 * "Unicode Standard Annex #9: THE BIDIRECTIONAL ALGORITHM"
26 * -- Copyright (C) 1999-2005, ASMUS, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining a
29 * copy of the Unicode data files and any associated documentation (the
30 * "Data Files") or Unicode software and any associated documentation (the
31 * "Software") to deal in the Data Files or Software without restriction,
32 * including without limitation the rights to use, copy, modify, merge,
33 * publish, distribute, and/or sell copies of the Data Files or Software,
34 * and to permit persons to whom the Data Files or Software are furnished
35 * to do so, provided that (a) the above copyright notice(s) and this
36 * permission notice appear with all copies of the Data Files or Software,
37 * (b) both the above copyright notice(s) and this permission notice appear
38 * in associated documentation, and (c) there is clear notice in each
39 * modified Data File or in the Software as well as in the documentation
40 * associated with the Data File(s) or Software that the data or software
52 #include "wine/unicode.h"
53 #include "wine/debug.h"
54 #include "gdi_private.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(bidi
);
58 /* HELPER FUNCTIONS AND DECLARATIONS */
60 #define odd(x) ((x) & 1)
62 /*------------------------------------------------------------------------
63 Bidirectional Character Types
65 as defined by the Unicode Bidirectional Algorithm Table 3-7.
69 The list of bidirectional character types here is not grouped the
70 same way as the table 3-7, since the numberic values for the types
71 are chosen to keep the state and action tables compact.
72 ------------------------------------------------------------------------*/
76 /* ON MUST be zero, code relies on ON = N = 0 */
77 ON
= 0, /* Other Neutral */
80 AN
, /* Arabic Number */
81 EN
, /* European Number */
82 AL
, /* Arabic Letter (Right-to-left) */
83 NSM
, /* Non-spacing Mark */
84 CS
, /* Common Separator */
85 ES
, /* European Separator */
86 ET
, /* European Terminator (post/prefix e.g. $ and %) */
89 BN
, /* Boundary neutral (type of RLE etc after explicit levels) */
92 S
, /* Segment Separator (TAB) // used only in L1 */
93 WS
, /* White space // used only in L1 */
94 B
, /* Paragraph Separator (aka as PS) */
96 /* types for explicit controls */
97 RLO
, /* these are used only in X1-X9 */
103 /* resolved types, also resolved directions */
104 N
= ON
, /* alias, where ON, WS and S are treated the same */
107 /* HELPER FUNCTIONS */
109 /* Convert the libwine information to the direction enum */
110 static void classify(LPCWSTR lpString
, WORD
*chartype
, DWORD uCount
)
112 static const enum directions dir_map
[16] =
114 L
, /* unassigned defaults to L */
129 PDF
/* also LRE, LRO, RLE, RLO */
134 for (i
= 0; i
< uCount
; ++i
)
136 chartype
[i
] = dir_map
[get_char_typeW(lpString
[i
]) >> 12];
137 if (chartype
[i
] == PDF
)
141 case 0x202A: chartype
[i
] = LRE
; break;
142 case 0x202B: chartype
[i
] = RLE
; break;
143 case 0x202C: chartype
[i
] = PDF
; break;
144 case 0x202D: chartype
[i
] = LRO
; break;
145 case 0x202E: chartype
[i
] = RLO
; break;
151 /* Set a run of cval values at locations all prior to, but not including */
152 /* iStart, to the new value nval. */
153 static void SetDeferredRun(BYTE
*pval
, int cval
, int iStart
, int nval
)
156 for (; i
>= iStart
- cval
; i
--)
162 /* THE PARAGRAPH LEVEL */
164 /*------------------------------------------------------------------------
165 Function: resolveParagraphs
167 Resolves the input strings into blocks over which the algorithm
170 Implements Rule P1 of the Unicode Bidi Algorithm
175 Output: revised character count
177 Note: This is a very simplistic function. In effect it restricts
178 the action of the algorithm to the first paragraph in the input
179 where a paragraph ends at the end of the first block separator
180 or at the end of the input text.
182 ------------------------------------------------------------------------*/
184 static int resolveParagraphs(WORD
*types
, int cch
)
186 /* skip characters not of type B */
188 for(; ich
< cch
&& types
[ich
] != B
; ich
++);
189 /* stop after first B, make it a BN for use in the next steps */
190 if (ich
< cch
&& types
[ich
] == B
)
196 /*------------------------------------------------------------------------
197 Function: resolveLines
199 Breaks a paragraph into lines
201 Input: Array of line break flags
203 In/Out: Array of characters
205 Returns the count of characters on the first line
207 Note: This function only breaks lines at hard line breaks. Other
208 line breaks can be passed in. If pbrk[n] is TRUE, then a break
209 occurs after the character in pszInput[n]. Breaks before the first
210 character are not allowed.
211 ------------------------------------------------------------------------*/
212 static int resolveLines(LPCWSTR pszInput
, const BOOL
* pbrk
, int cch
)
214 /* skip characters not of type LS */
216 for(; ich
< cch
; ich
++)
218 if (pszInput
[ich
] == (WCHAR
)'\n' || (pbrk
&& pbrk
[ich
]))
228 /*------------------------------------------------------------------------
229 Function: resolveWhiteSpace
231 Resolves levels for WS and S
232 Implements rule L1 of the Unicode bidi Algorithm.
234 Input: Base embedding level
236 Array of direction classes (for one line of text)
238 In/Out: Array of embedding levels (for one line of text)
240 Note: this should be applied a line at a time. The default driver
241 code supplied in this file assumes a single line of text; for
242 a real implementation, cch and the initial pointer values
243 would have to be adjusted.
244 ------------------------------------------------------------------------*/
245 static void resolveWhitespace(int baselevel
, const WORD
*pcls
, BYTE
*plevel
, int cch
)
248 BYTE oldlevel
= baselevel
;
251 for (; ich
< cch
; ich
++)
256 cchrun
= 0; /* any other character breaks the run */
268 plevel
[ich
] = oldlevel
;
274 /* reset levels for WS before eot */
275 SetDeferredRun(plevel
, cchrun
, ich
, baselevel
);
277 plevel
[ich
] = baselevel
;
280 oldlevel
= plevel
[ich
];
282 /* reset level before eot */
283 SetDeferredRun(plevel
, cchrun
, ich
, baselevel
);
286 /*------------------------------------------------------------------------
289 Implements the Line-by-Line phases of the Unicode Bidi Algorithm
291 Input: Count of characters
292 Array of character directions
297 ------------------------------------------------------------------------*/
298 static void BidiLines(int baselevel
, LPWSTR pszOutLine
, LPCWSTR pszLine
, const WORD
* pclsLine
,
299 BYTE
* plevelLine
, int cchPara
, const BOOL
* pbrk
)
305 run
= HeapAlloc(GetProcessHeap(), 0, cchPara
* sizeof(int));
308 WARN("Out of memory\n");
314 /* break lines at LS */
315 cchLine
= resolveLines(pszLine
, pbrk
, cchPara
);
317 /* resolve whitespace */
318 resolveWhitespace(baselevel
, pclsLine
, plevelLine
, cchLine
);
323 /* reorder each line in place */
324 ScriptLayout(cchLine
, plevelLine
, NULL
, run
);
325 for (i
= 0; i
< cchLine
; i
++)
326 pszOutLine
[done
+run
[i
]] = pszLine
[i
];
330 plevelLine
+= cchLine
;
331 pbrk
+= pbrk
? cchLine
: 0;
338 HeapFree(GetProcessHeap(), 0, run
);
341 /*************************************************************
344 * Returns TRUE if reordering was required and done.
347 HDC hDC
, /*[in] Display DC */
348 LPCWSTR lpString
, /* [in] The string for which information is to be returned */
349 INT uCount
, /* [in] Number of WCHARs in string. */
350 DWORD dwFlags
, /* [in] GetCharacterPlacement compatible flags specifying how to process the string */
351 DWORD dwWineGCP_Flags
, /* [in] Wine internal flags - Force paragraph direction */
352 LPWSTR lpOutString
, /* [out] Reordered string */
353 INT uCountOut
, /* [in] Size of output buffer */
354 UINT
*lpOrder
, /* [out] Logical -> Visual order map */
355 WORD
**lpGlyphs
, /* [out] reordered, mirrored, shaped glyphs to display */
356 INT
*cGlyphs
/* [out] number of glyphs generated */
367 SCRIPT_CONTROL Control
;
371 SCRIPT_CACHE psc
= NULL
;
372 WORD
*run_glyphs
= NULL
;
373 WORD
*pwLogClust
= NULL
;
374 SCRIPT_VISATTR
*psva
= NULL
;
375 DWORD cMaxGlyphs
= 0;
376 BOOL doGlyphs
= TRUE
;
378 TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
379 debugstr_wn(lpString
, uCount
), uCount
, dwFlags
,
380 lpOutString
, lpOrder
);
382 memset(&Control
, 0, sizeof(Control
));
383 memset(&State
, 0, sizeof(State
));
387 if (!(dwFlags
& GCP_REORDER
))
389 FIXME("Asked to reorder without reorder flag set\n");
393 if (lpOutString
&& uCountOut
< uCount
)
395 FIXME("lpOutString too small\n");
399 chartype
= HeapAlloc(GetProcessHeap(), 0, uCount
* sizeof(WORD
));
402 WARN("Out of memory\n");
407 memcpy(lpOutString
, lpString
, uCount
* sizeof(WCHAR
));
410 for (i
= 0; i
< uCount
&& !is_complex
; i
++)
412 if ((lpString
[i
] >= 0x900 && lpString
[i
] <= 0xfff) ||
413 (lpString
[i
] >= 0x1cd0 && lpString
[i
] <= 0x1cff) ||
414 (lpString
[i
] >= 0xa840 && lpString
[i
] <= 0xa8ff))
418 /* Verify reordering will be required */
419 if ((WINE_GCPW_FORCE_RTL
== (dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
)) ||
420 ((dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
) == WINE_GCPW_LOOSE_RTL
))
421 State
.uBidiLevel
= 1;
422 else if (!is_complex
)
425 classify(lpString
, chartype
, uCount
);
426 for (i
= 0; i
< uCount
; i
++)
438 HeapFree(GetProcessHeap(), 0, chartype
);
441 for (i
= 0; i
< uCount
; i
++)
448 levels
= HeapAlloc(GetProcessHeap(), 0, uCount
* sizeof(BYTE
));
451 WARN("Out of memory\n");
452 HeapFree(GetProcessHeap(), 0, chartype
);
457 pItems
= HeapAlloc(GetProcessHeap(),0, maxItems
* sizeof(SCRIPT_ITEM
));
460 WARN("Out of memory\n");
461 HeapFree(GetProcessHeap(), 0, chartype
);
462 HeapFree(GetProcessHeap(), 0, levels
);
468 cMaxGlyphs
= 1.5 * uCount
+ 16;
469 run_glyphs
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * cMaxGlyphs
);
472 WARN("Out of memory\n");
473 HeapFree(GetProcessHeap(), 0, chartype
);
474 HeapFree(GetProcessHeap(), 0, levels
);
475 HeapFree(GetProcessHeap(), 0, pItems
);
478 pwLogClust
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * uCount
);
481 WARN("Out of memory\n");
482 HeapFree(GetProcessHeap(), 0, chartype
);
483 HeapFree(GetProcessHeap(), 0, levels
);
484 HeapFree(GetProcessHeap(), 0, pItems
);
485 HeapFree(GetProcessHeap(), 0, run_glyphs
);
488 psva
= HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR
) * uCount
);
491 WARN("Out of memory\n");
492 HeapFree(GetProcessHeap(), 0, chartype
);
493 HeapFree(GetProcessHeap(), 0, levels
);
494 HeapFree(GetProcessHeap(), 0, pItems
);
495 HeapFree(GetProcessHeap(), 0, run_glyphs
);
496 HeapFree(GetProcessHeap(), 0, pwLogClust
);
503 while (done
< uCount
)
506 classify(lpString
+ done
, chartype
, uCount
- done
);
507 /* limit text to first block */
508 i
= resolveParagraphs(chartype
, uCount
- done
);
509 for (j
= 0; j
< i
; ++j
)
515 case ON
: chartype
[j
] = N
;
519 if ((dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
) == WINE_GCPW_LOOSE_RTL
)
520 State
.uBidiLevel
= 1;
521 else if ((dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
) == WINE_GCPW_LOOSE_LTR
)
522 State
.uBidiLevel
= 0;
524 if (dwWineGCP_Flags
& WINE_GCPW_LOOSE_MASK
)
526 for (j
= 0; j
< i
; ++j
)
527 if (chartype
[j
] == L
)
529 State
.uBidiLevel
= 0;
532 else if (chartype
[j
] == R
|| chartype
[j
] == AL
)
534 State
.uBidiLevel
= 1;
539 res
= ScriptItemize(lpString
+ done
, i
, maxItems
, &Control
, &State
, pItems
, &nItems
);
540 while (res
== E_OUTOFMEMORY
)
542 maxItems
= maxItems
* 2;
543 pItems
= HeapReAlloc(GetProcessHeap(), 0, pItems
, sizeof(SCRIPT_ITEM
) * maxItems
);
546 WARN("Out of memory\n");
547 HeapFree(GetProcessHeap(), 0, chartype
);
548 HeapFree(GetProcessHeap(), 0, levels
);
549 HeapFree(GetProcessHeap(), 0, run_glyphs
);
550 HeapFree(GetProcessHeap(), 0, pwLogClust
);
551 HeapFree(GetProcessHeap(), 0, psva
);
554 res
= ScriptItemize(lpString
+ done
, i
, maxItems
, &Control
, &State
, pItems
, &nItems
);
557 if (lpOutString
|| lpOrder
)
558 for (j
= 0; j
< nItems
; j
++)
561 for (k
= pItems
[j
].iCharPos
; k
< pItems
[j
+1].iCharPos
; k
++)
562 levels
[k
] = pItems
[j
].a
.s
.uBidiLevel
;
567 /* assign directional types again, but for WS, S this time */
568 classify(lpString
+ done
, chartype
, i
);
570 BidiLines(State
.uBidiLevel
, lpOutString
+ done
, lpString
+ done
,
571 chartype
, levels
, i
, 0);
577 for (j
= lastgood
= 0; j
< i
; ++j
)
578 if (levels
[j
] != levels
[lastgood
])
581 if (odd(levels
[lastgood
]))
582 for (k
= j
; k
>= lastgood
; --k
)
583 lpOrder
[done
+ k
] = done
+ j
- k
;
585 for (k
= lastgood
; k
<= j
; ++k
)
586 lpOrder
[done
+ k
] = done
+ k
;
589 if (odd(levels
[lastgood
]))
590 for (k
= j
- 1; k
>= lastgood
; --k
)
591 lpOrder
[done
+ k
] = done
+ j
- 1 - k
;
593 for (k
= lastgood
; k
< j
; ++k
)
594 lpOrder
[done
+ k
] = done
+ k
;
597 if (lpGlyphs
&& doGlyphs
)
601 SCRIPT_ITEM
*curItem
;
603 runOrder
= HeapAlloc(GetProcessHeap(), 0, maxItems
* sizeof(*runOrder
));
604 visOrder
= HeapAlloc(GetProcessHeap(), 0, maxItems
* sizeof(*visOrder
));
605 if (!runOrder
|| !visOrder
)
607 WARN("Out of memory\n");
608 HeapFree(GetProcessHeap(), 0, runOrder
);
609 HeapFree(GetProcessHeap(), 0, visOrder
);
610 HeapFree(GetProcessHeap(), 0, chartype
);
611 HeapFree(GetProcessHeap(), 0, levels
);
612 HeapFree(GetProcessHeap(), 0, pItems
);
613 HeapFree(GetProcessHeap(), 0, psva
);
614 HeapFree(GetProcessHeap(), 0, pwLogClust
);
618 for (j
= 0; j
< nItems
; j
++)
619 runOrder
[j
] = pItems
[j
].a
.s
.uBidiLevel
;
621 ScriptLayout(nItems
, runOrder
, visOrder
, NULL
);
623 for (j
= 0; j
< nItems
; j
++)
626 int cChars
,cOutGlyphs
;
627 curItem
= &pItems
[visOrder
[j
]];
629 cChars
= pItems
[visOrder
[j
]+1].iCharPos
- curItem
->iCharPos
;
631 res
= ScriptShape(hDC
, &psc
, lpString
+ done
+ curItem
->iCharPos
, cChars
, cMaxGlyphs
, &curItem
->a
, run_glyphs
, pwLogClust
, psva
, &cOutGlyphs
);
632 while (res
== E_OUTOFMEMORY
)
635 run_glyphs
= HeapReAlloc(GetProcessHeap(), 0, run_glyphs
, sizeof(WORD
) * cMaxGlyphs
);
638 WARN("Out of memory\n");
639 HeapFree(GetProcessHeap(), 0, runOrder
);
640 HeapFree(GetProcessHeap(), 0, visOrder
);
641 HeapFree(GetProcessHeap(), 0, chartype
);
642 HeapFree(GetProcessHeap(), 0, levels
);
643 HeapFree(GetProcessHeap(), 0, pItems
);
644 HeapFree(GetProcessHeap(), 0, psva
);
645 HeapFree(GetProcessHeap(), 0, pwLogClust
);
646 HeapFree(GetProcessHeap(), 0, *lpGlyphs
);
647 ScriptFreeCache(&psc
);
651 res
= ScriptShape(hDC
, &psc
, lpString
+ done
+ curItem
->iCharPos
, cChars
, cMaxGlyphs
, &curItem
->a
, run_glyphs
, pwLogClust
, psva
, &cOutGlyphs
);
655 if (res
== USP_E_SCRIPT_NOT_IN_FONT
)
656 TRACE("Unable to shape with currently selected font\n");
658 FIXME("Unable to shape string (%x)\n",res
);
661 HeapFree(GetProcessHeap(), 0, *lpGlyphs
);
667 *lpGlyphs
= HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs
, sizeof(WORD
) * (glyph_i
+ cOutGlyphs
));
669 *lpGlyphs
= HeapAlloc(GetProcessHeap(), 0, sizeof(WORD
) * (glyph_i
+ cOutGlyphs
));
670 for (k
= 0; k
< cOutGlyphs
; k
++)
671 (*lpGlyphs
)[glyph_i
+k
] = run_glyphs
[k
];
672 glyph_i
+= cOutGlyphs
;
675 HeapFree(GetProcessHeap(), 0, runOrder
);
676 HeapFree(GetProcessHeap(), 0, visOrder
);
684 HeapFree(GetProcessHeap(), 0, chartype
);
685 HeapFree(GetProcessHeap(), 0, levels
);
686 HeapFree(GetProcessHeap(), 0, pItems
);
687 HeapFree(GetProcessHeap(), 0, run_glyphs
);
688 HeapFree(GetProcessHeap(), 0, pwLogClust
);
689 HeapFree(GetProcessHeap(), 0, psva
);
690 ScriptFreeCache(&psc
);