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
50 #include "wine/debug.h"
51 #include "gdi_private.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(bidi
);
55 /* HELPER FUNCTIONS AND DECLARATIONS */
57 #define odd(x) ((x) & 1)
59 extern const unsigned short bidi_direction_table
[] DECLSPEC_HIDDEN
;
61 /*------------------------------------------------------------------------
62 Bidirectional Character Types
64 as defined by the Unicode Bidirectional Algorithm Table 3-7.
68 The list of bidirectional character types here is not grouped the
69 same way as the table 3-7, since the numeric values for the types
70 are chosen to keep the state and action tables compact.
71 ------------------------------------------------------------------------*/
75 /* ON MUST be zero, code relies on ON = N = 0 */
76 ON
= 0, /* Other Neutral */
79 AN
, /* Arabic Number */
80 EN
, /* European Number */
81 AL
, /* Arabic Letter (Right-to-left) */
82 NSM
, /* Non-spacing Mark */
83 CS
, /* Common Separator */
84 ES
, /* European Separator */
85 ET
, /* European Terminator (post/prefix e.g. $ and %) */
88 BN
, /* Boundary neutral (type of RLE etc after explicit levels) */
91 S
, /* Segment Separator (TAB) // used only in L1 */
92 WS
, /* White space // used only in L1 */
93 B
, /* Paragraph Separator (aka as PS) */
95 /* types for explicit controls */
96 RLO
, /* these are used only in X1-X9 */
102 LRI
, /* Isolate formatting characters new with 6.3 */
107 /* resolved types, also resolved directions */
108 NI
= ON
, /* alias, where ON, WS and S are treated the same */
111 /* HELPER FUNCTIONS */
113 static inline unsigned short get_table_entry(const unsigned short *table
, WCHAR ch
)
115 return table
[table
[table
[ch
>> 8] + ((ch
>> 4) & 0x0f)] + (ch
& 0xf)];
118 /* Convert the libwine information to the direction enum */
119 static void classify(LPCWSTR lpString
, WORD
*chartype
, DWORD uCount
)
123 for (i
= 0; i
< uCount
; ++i
)
124 chartype
[i
] = get_table_entry( bidi_direction_table
, lpString
[i
] );
127 /* Set a run of cval values at locations all prior to, but not including */
128 /* iStart, to the new value nval. */
129 static void SetDeferredRun(BYTE
*pval
, int cval
, int iStart
, int nval
)
132 for (; i
>= iStart
- cval
; i
--)
138 /* THE PARAGRAPH LEVEL */
140 /*------------------------------------------------------------------------
141 Function: resolveParagraphs
143 Resolves the input strings into blocks over which the algorithm
146 Implements Rule P1 of the Unicode Bidi Algorithm
151 Output: revised character count
153 Note: This is a very simplistic function. In effect it restricts
154 the action of the algorithm to the first paragraph in the input
155 where a paragraph ends at the end of the first block separator
156 or at the end of the input text.
158 ------------------------------------------------------------------------*/
160 static int resolveParagraphs(WORD
*types
, int cch
)
162 /* skip characters not of type B */
164 for(; ich
< cch
&& types
[ich
] != B
; ich
++);
165 /* stop after first B, make it a BN for use in the next steps */
166 if (ich
< cch
&& types
[ich
] == B
)
172 /*------------------------------------------------------------------------
173 Function: resolveLines
175 Breaks a paragraph into lines
177 Input: Array of line break flags
179 In/Out: Array of characters
181 Returns the count of characters on the first line
183 Note: This function only breaks lines at hard line breaks. Other
184 line breaks can be passed in. If pbrk[n] is TRUE, then a break
185 occurs after the character in pszInput[n]. Breaks before the first
186 character are not allowed.
187 ------------------------------------------------------------------------*/
188 static int resolveLines(LPCWSTR pszInput
, const BOOL
* pbrk
, int cch
)
190 /* skip characters not of type LS */
192 for(; ich
< cch
; ich
++)
194 if (pszInput
[ich
] == (WCHAR
)'\n' || (pbrk
&& pbrk
[ich
]))
204 /*------------------------------------------------------------------------
205 Function: resolveWhiteSpace
207 Resolves levels for WS and S
208 Implements rule L1 of the Unicode bidi Algorithm.
210 Input: Base embedding level
212 Array of direction classes (for one line of text)
214 In/Out: Array of embedding levels (for one line of text)
216 Note: this should be applied a line at a time. The default driver
217 code supplied in this file assumes a single line of text; for
218 a real implementation, cch and the initial pointer values
219 would have to be adjusted.
220 ------------------------------------------------------------------------*/
221 static void resolveWhitespace(int baselevel
, const WORD
*pcls
, BYTE
*plevel
, int cch
)
224 BYTE oldlevel
= baselevel
;
227 for (; ich
< cch
; ich
++)
232 cchrun
= 0; /* any other character breaks the run */
248 plevel
[ich
] = oldlevel
;
254 /* reset levels for WS before eot */
255 SetDeferredRun(plevel
, cchrun
, ich
, baselevel
);
257 plevel
[ich
] = baselevel
;
260 oldlevel
= plevel
[ich
];
262 /* reset level before eot */
263 SetDeferredRun(plevel
, cchrun
, ich
, baselevel
);
266 /*------------------------------------------------------------------------
269 Implements the Line-by-Line phases of the Unicode Bidi Algorithm
271 Input: Count of characters
272 Array of character directions
277 ------------------------------------------------------------------------*/
278 static void BidiLines(int baselevel
, LPWSTR pszOutLine
, LPCWSTR pszLine
, const WORD
* pclsLine
,
279 BYTE
* plevelLine
, int cchPara
, const BOOL
* pbrk
)
285 run
= HeapAlloc(GetProcessHeap(), 0, cchPara
* sizeof(int));
288 WARN("Out of memory\n");
294 /* break lines at LS */
295 cchLine
= resolveLines(pszLine
, pbrk
, cchPara
);
297 /* resolve whitespace */
298 resolveWhitespace(baselevel
, pclsLine
, plevelLine
, cchLine
);
303 /* reorder each line in place */
304 ScriptLayout(cchLine
, plevelLine
, NULL
, run
);
305 for (i
= 0; i
< cchLine
; i
++)
306 pszOutLine
[done
+run
[i
]] = pszLine
[i
];
310 plevelLine
+= cchLine
;
311 pbrk
+= pbrk
? cchLine
: 0;
318 HeapFree(GetProcessHeap(), 0, run
);
321 /*************************************************************
324 * Returns TRUE if reordering was required and done.
327 HDC hDC
, /*[in] Display DC */
328 LPCWSTR lpString
, /* [in] The string for which information is to be returned */
329 INT uCount
, /* [in] Number of WCHARs in string. */
330 DWORD dwFlags
, /* [in] GetCharacterPlacement compatible flags specifying how to process the string */
331 DWORD dwWineGCP_Flags
, /* [in] Wine internal flags - Force paragraph direction */
332 LPWSTR lpOutString
, /* [out] Reordered string */
333 INT uCountOut
, /* [in] Size of output buffer */
334 UINT
*lpOrder
, /* [out] Logical -> Visual order map */
335 WORD
**lpGlyphs
, /* [out] reordered, mirrored, shaped glyphs to display */
336 INT
*cGlyphs
/* [out] number of glyphs generated */
347 SCRIPT_CONTROL Control
;
351 SCRIPT_CACHE psc
= NULL
;
352 WORD
*run_glyphs
= NULL
;
353 WORD
*pwLogClust
= NULL
;
354 SCRIPT_VISATTR
*psva
= NULL
;
355 DWORD cMaxGlyphs
= 0;
356 BOOL doGlyphs
= TRUE
;
358 TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
359 debugstr_wn(lpString
, uCount
), uCount
, dwFlags
,
360 lpOutString
, lpOrder
);
362 memset(&Control
, 0, sizeof(Control
));
363 memset(&State
, 0, sizeof(State
));
367 if (!(dwFlags
& GCP_REORDER
))
369 FIXME("Asked to reorder without reorder flag set\n");
373 if (lpOutString
&& uCountOut
< uCount
)
375 FIXME("lpOutString too small\n");
379 chartype
= HeapAlloc(GetProcessHeap(), 0, uCount
* sizeof(WORD
));
382 WARN("Out of memory\n");
387 memcpy(lpOutString
, lpString
, uCount
* sizeof(WCHAR
));
390 for (i
= 0; i
< uCount
&& !is_complex
; i
++)
392 if ((lpString
[i
] >= 0x900 && lpString
[i
] <= 0xfff) ||
393 (lpString
[i
] >= 0x1cd0 && lpString
[i
] <= 0x1cff) ||
394 (lpString
[i
] >= 0xa840 && lpString
[i
] <= 0xa8ff))
398 /* Verify reordering will be required */
399 if ((WINE_GCPW_FORCE_RTL
== (dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
)) ||
400 ((dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
) == WINE_GCPW_LOOSE_RTL
))
401 State
.uBidiLevel
= 1;
402 else if (!is_complex
)
405 classify(lpString
, chartype
, uCount
);
406 for (i
= 0; i
< uCount
; i
++)
418 HeapFree(GetProcessHeap(), 0, chartype
);
421 for (i
= 0; i
< uCount
; i
++)
428 levels
= HeapAlloc(GetProcessHeap(), 0, uCount
* sizeof(BYTE
));
431 WARN("Out of memory\n");
432 HeapFree(GetProcessHeap(), 0, chartype
);
437 pItems
= HeapAlloc(GetProcessHeap(),0, maxItems
* sizeof(SCRIPT_ITEM
));
440 WARN("Out of memory\n");
441 HeapFree(GetProcessHeap(), 0, chartype
);
442 HeapFree(GetProcessHeap(), 0, levels
);
448 cMaxGlyphs
= 1.5 * uCount
+ 16;
449 run_glyphs
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * cMaxGlyphs
);
452 WARN("Out of memory\n");
453 HeapFree(GetProcessHeap(), 0, chartype
);
454 HeapFree(GetProcessHeap(), 0, levels
);
455 HeapFree(GetProcessHeap(), 0, pItems
);
458 pwLogClust
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * uCount
);
461 WARN("Out of memory\n");
462 HeapFree(GetProcessHeap(), 0, chartype
);
463 HeapFree(GetProcessHeap(), 0, levels
);
464 HeapFree(GetProcessHeap(), 0, pItems
);
465 HeapFree(GetProcessHeap(), 0, run_glyphs
);
468 psva
= HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR
) * uCount
);
471 WARN("Out of memory\n");
472 HeapFree(GetProcessHeap(), 0, chartype
);
473 HeapFree(GetProcessHeap(), 0, levels
);
474 HeapFree(GetProcessHeap(), 0, pItems
);
475 HeapFree(GetProcessHeap(), 0, run_glyphs
);
476 HeapFree(GetProcessHeap(), 0, pwLogClust
);
483 while (done
< uCount
)
486 classify(lpString
+ done
, chartype
, uCount
- done
);
487 /* limit text to first block */
488 i
= resolveParagraphs(chartype
, uCount
- done
);
489 for (j
= 0; j
< i
; ++j
)
495 case ON
: chartype
[j
] = NI
;
499 if ((dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
) == WINE_GCPW_LOOSE_RTL
)
500 State
.uBidiLevel
= 1;
501 else if ((dwWineGCP_Flags
&WINE_GCPW_DIR_MASK
) == WINE_GCPW_LOOSE_LTR
)
502 State
.uBidiLevel
= 0;
504 if (dwWineGCP_Flags
& WINE_GCPW_LOOSE_MASK
)
506 for (j
= 0; j
< i
; ++j
)
507 if (chartype
[j
] == L
)
509 State
.uBidiLevel
= 0;
512 else if (chartype
[j
] == R
|| chartype
[j
] == AL
)
514 State
.uBidiLevel
= 1;
519 res
= ScriptItemize(lpString
+ done
, i
, maxItems
, &Control
, &State
, pItems
, &nItems
);
520 while (res
== E_OUTOFMEMORY
)
522 maxItems
= maxItems
* 2;
523 pItems
= HeapReAlloc(GetProcessHeap(), 0, pItems
, sizeof(SCRIPT_ITEM
) * maxItems
);
526 WARN("Out of memory\n");
527 HeapFree(GetProcessHeap(), 0, chartype
);
528 HeapFree(GetProcessHeap(), 0, levels
);
529 HeapFree(GetProcessHeap(), 0, run_glyphs
);
530 HeapFree(GetProcessHeap(), 0, pwLogClust
);
531 HeapFree(GetProcessHeap(), 0, psva
);
534 res
= ScriptItemize(lpString
+ done
, i
, maxItems
, &Control
, &State
, pItems
, &nItems
);
537 if (lpOutString
|| lpOrder
)
538 for (j
= 0; j
< nItems
; j
++)
541 for (k
= pItems
[j
].iCharPos
; k
< pItems
[j
+1].iCharPos
; k
++)
542 levels
[k
] = pItems
[j
].a
.s
.uBidiLevel
;
547 /* assign directional types again, but for WS, S this time */
548 classify(lpString
+ done
, chartype
, i
);
550 BidiLines(State
.uBidiLevel
, lpOutString
+ done
, lpString
+ done
,
551 chartype
, levels
, i
, 0);
557 for (j
= lastgood
= 0; j
< i
; ++j
)
558 if (levels
[j
] != levels
[lastgood
])
561 if (odd(levels
[lastgood
]))
562 for (k
= j
; k
>= lastgood
; --k
)
563 lpOrder
[done
+ k
] = done
+ j
- k
;
565 for (k
= lastgood
; k
<= j
; ++k
)
566 lpOrder
[done
+ k
] = done
+ k
;
569 if (odd(levels
[lastgood
]))
570 for (k
= j
- 1; k
>= lastgood
; --k
)
571 lpOrder
[done
+ k
] = done
+ j
- 1 - k
;
573 for (k
= lastgood
; k
< j
; ++k
)
574 lpOrder
[done
+ k
] = done
+ k
;
577 if (lpGlyphs
&& doGlyphs
)
581 SCRIPT_ITEM
*curItem
;
583 runOrder
= HeapAlloc(GetProcessHeap(), 0, maxItems
* sizeof(*runOrder
));
584 visOrder
= HeapAlloc(GetProcessHeap(), 0, maxItems
* sizeof(*visOrder
));
585 if (!runOrder
|| !visOrder
)
587 WARN("Out of memory\n");
588 HeapFree(GetProcessHeap(), 0, runOrder
);
589 HeapFree(GetProcessHeap(), 0, visOrder
);
590 HeapFree(GetProcessHeap(), 0, chartype
);
591 HeapFree(GetProcessHeap(), 0, levels
);
592 HeapFree(GetProcessHeap(), 0, pItems
);
593 HeapFree(GetProcessHeap(), 0, psva
);
594 HeapFree(GetProcessHeap(), 0, pwLogClust
);
598 for (j
= 0; j
< nItems
; j
++)
599 runOrder
[j
] = pItems
[j
].a
.s
.uBidiLevel
;
601 ScriptLayout(nItems
, runOrder
, visOrder
, NULL
);
603 for (j
= 0; j
< nItems
; j
++)
606 int cChars
,cOutGlyphs
;
607 curItem
= &pItems
[visOrder
[j
]];
609 cChars
= pItems
[visOrder
[j
]+1].iCharPos
- curItem
->iCharPos
;
611 res
= ScriptShape(hDC
, &psc
, lpString
+ done
+ curItem
->iCharPos
, cChars
, cMaxGlyphs
, &curItem
->a
, run_glyphs
, pwLogClust
, psva
, &cOutGlyphs
);
612 while (res
== E_OUTOFMEMORY
)
615 run_glyphs
= HeapReAlloc(GetProcessHeap(), 0, run_glyphs
, sizeof(WORD
) * cMaxGlyphs
);
618 WARN("Out of memory\n");
619 HeapFree(GetProcessHeap(), 0, runOrder
);
620 HeapFree(GetProcessHeap(), 0, visOrder
);
621 HeapFree(GetProcessHeap(), 0, chartype
);
622 HeapFree(GetProcessHeap(), 0, levels
);
623 HeapFree(GetProcessHeap(), 0, pItems
);
624 HeapFree(GetProcessHeap(), 0, psva
);
625 HeapFree(GetProcessHeap(), 0, pwLogClust
);
626 HeapFree(GetProcessHeap(), 0, *lpGlyphs
);
627 ScriptFreeCache(&psc
);
631 res
= ScriptShape(hDC
, &psc
, lpString
+ done
+ curItem
->iCharPos
, cChars
, cMaxGlyphs
, &curItem
->a
, run_glyphs
, pwLogClust
, psva
, &cOutGlyphs
);
635 if (res
== USP_E_SCRIPT_NOT_IN_FONT
)
636 TRACE("Unable to shape with currently selected font\n");
638 FIXME("Unable to shape string (%x)\n",res
);
641 HeapFree(GetProcessHeap(), 0, *lpGlyphs
);
647 *lpGlyphs
= HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs
, sizeof(WORD
) * (glyph_i
+ cOutGlyphs
));
649 *lpGlyphs
= HeapAlloc(GetProcessHeap(), 0, sizeof(WORD
) * (glyph_i
+ cOutGlyphs
));
650 for (k
= 0; k
< cOutGlyphs
; k
++)
651 (*lpGlyphs
)[glyph_i
+k
] = run_glyphs
[k
];
652 glyph_i
+= cOutGlyphs
;
655 HeapFree(GetProcessHeap(), 0, runOrder
);
656 HeapFree(GetProcessHeap(), 0, visOrder
);
664 HeapFree(GetProcessHeap(), 0, chartype
);
665 HeapFree(GetProcessHeap(), 0, levels
);
666 HeapFree(GetProcessHeap(), 0, pItems
);
667 HeapFree(GetProcessHeap(), 0, run_glyphs
);
668 HeapFree(GetProcessHeap(), 0, pwLogClust
);
669 HeapFree(GetProcessHeap(), 0, psva
);
670 ScriptFreeCache(&psc
);