xref: /aoo41x/main/vcl/source/gdi/sallayout.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include <cstdio>
32 
33 #define _USE_MATH_DEFINES
34 #include <math.h>
35 #include <sal/alloca.h>
36 
37 #include <salgdi.hxx>
38 #include <sallayout.hxx>
39 
40 #include <basegfx/polygon/b2dpolypolygon.hxx>
41 #include <basegfx/matrix/b2dhommatrix.hxx>
42 #include <basegfx/matrix/b2dhommatrixtools.hxx>
43 
44 #include <i18npool/lang.h>
45 
46 #include <tools/debug.hxx>
47 
48 #include <limits.h>
49 
50 #if defined _MSC_VER
51 #pragma warning(push, 1)
52 #endif
53 #include <unicode/ubidi.h>
54 #include <unicode/uchar.h>
55 #if defined _MSC_VER
56 #pragma warning(pop)
57 #endif
58 
59 #include <algorithm>
60 
61 #ifdef DEBUG
62 //#define MULTI_SL_DEBUG
63 #endif
64 
65 #ifdef MULTI_SL_DEBUG
66 #include <string>
67 FILE * mslLogFile = NULL;
68 FILE * mslLog()
69 {
70 #ifdef MSC
71 	std::string logFileName(getenv("TEMP"));
72 	logFileName.append("\\msllayout.log");
73     if (mslLogFile == NULL) mslLogFile = fopen(logFileName.c_str(),"w");
74     else fflush(mslLogFile);
75     return mslLogFile;
76 #else
77     return stdout;
78 #endif
79 }
80 #endif
81 // =======================================================================
82 
83 // TODO: ask the glyph directly, for now we need this method because of #i99367#
84 // true if a codepoint doesn't influence the logical text width
85 bool IsDiacritic( sal_UCS4 nChar )
86 {
87 	// shortcut abvious non-diacritics
88 	if( nChar < 0x0300 )
89 		return false;
90  	if( nChar >= 0x2100 )
91 		return false;
92 
93 	// TODO: #i105058# use icu uchar.h's character classification instead of the handcrafted table
94 	struct DiaRange { sal_UCS4 mnMin, mnEnd;};
95 	static const DiaRange aRanges[] = {
96 		{0x0300, 0x0370},
97 		{0x0590, 0x05BE}, {0x05BF, 0x05C0}, {0x05C1, 0x05C3}, {0x05C4, 0x05C6}, {0x05C7, 0x05C8},
98 		{0x0610, 0x061B}, {0x064B, 0x0660}, {0x0670, 0x0671}, {0x06D6, 0x06DD}, {0x06DF, 0x06E5}, {0x06E7, 0x06E9}, {0x06EA,0x06EF},
99 		{0x0730, 0x074D}, {0x07A6, 0x07B1}, {0x07EB, 0x07F4},
100 #if 0 // all known fonts have zero-width diacritics already, so no need to query it
101 		{0x0900, 0x0904}, {0x093C, 0x093D}, {0x0941, 0x0948}, {0x094D, 0x0950}, {0x0951, 0x0958},
102 		{0x0980, 0x0985}, {0x09BC, 0x09BD}, {0x09C1, 0x09C7}, {0x09CD, 0x09CE}, {0x09E2, 0x09E6},
103 		{0x0A00, 0x0A05}, {0x0A3C, 0x0A59}, //...
104 #endif
105 		{0x1DC0, 0x1E00},
106 		{0x205F, 0x2070}, {0x20D0, 0x2100},
107 		{0xFB1E, 0xFB1F}
108 	};
109 
110 	// TODO: almost anything is faster than an O(n) search
111 	static const int nCount = sizeof(aRanges) / sizeof(*aRanges);
112 	const DiaRange* pRange = &aRanges[0];
113 	for( int i = nCount; --i >= 0; ++pRange )
114 		if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
115 			return true;
116 
117 	return false;
118 }
119 
120 // =======================================================================
121 
122 int GetVerticalFlags( sal_UCS4 nChar )
123 {
124     if( (nChar >= 0x1100 && nChar <= 0x11f9)    // Hangul Jamo
125      || (nChar == 0x2030 || nChar == 0x2031)    // per mille sign
126      || (nChar >= 0x3000 && nChar <= 0xfaff)    // unified CJK
127      || (nChar >= 0xfe20 && nChar <= 0xfe6f)    // CJK compatibility
128      || (nChar >= 0xff00 && nChar <= 0xfffd) )  // other CJK
129     {
130         /* #i52932# remember:
131          nChar == 0x2010 || nChar == 0x2015
132          nChar == 0x2016 || nChar == 0x2026
133          are GF_NONE also, but already handled in the outer if condition
134         */
135         if((nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012)
136         || (nChar == 0xFF3B || nChar == 0xFF3D)
137         || (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms
138         || (nChar == 0xFFE3) )
139             return GF_NONE; // not rotated
140         else if( nChar == 0x30fc )
141             return GF_ROTR; // right
142         return GF_ROTL;     // left
143     }
144     else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs
145         return GF_ROTL; // left
146 
147     return GF_NONE; // not rotated as default
148 }
149 
150 // -----------------------------------------------------------------------
151 
152 sal_UCS4 GetVerticalChar( sal_UCS4 )
153 {
154     return 0; // #i14788# input method is responsible vertical char changes
155 
156 #if 0
157 	int nVert = 0;
158     switch( nChar )
159     {
160         // #104627# special treatment for some unicodes
161         case 0x002C: nVert = 0x3001; break;
162         case 0x002E: nVert = 0x3002; break;
163 		/*
164 		// to few fonts have the compatibility forms, using
165         // them will then cause more trouble than good
166         // TODO: decide on a font specific basis
167         case 0x2018: nVert = 0xFE41; break;
168         case 0x2019: nVert = 0xFE42; break;
169         case 0x201C: nVert = 0xFE43; break;
170         case 0x201D: nVert = 0xFE44; break;
171         // CJK compatibility forms
172         case 0x2025: nVert = 0xFE30; break;
173         case 0x2014: nVert = 0xFE31; break;
174         case 0x2013: nVert = 0xFE32; break;
175         case 0x005F: nVert = 0xFE33; break;
176         case 0x0028: nVert = 0xFE35; break;
177         case 0x0029: nVert = 0xFE36; break;
178         case 0x007B: nVert = 0xFE37; break;
179         case 0x007D: nVert = 0xFE38; break;
180         case 0x3014: nVert = 0xFE39; break;
181         case 0x3015: nVert = 0xFE3A; break;
182         case 0x3010: nVert = 0xFE3B; break;
183         case 0x3011: nVert = 0xFE3C; break;
184         case 0x300A: nVert = 0xFE3D; break;
185         case 0x300B: nVert = 0xFE3E; break;
186         case 0x3008: nVert = 0xFE3F; break;
187         case 0x3009: nVert = 0xFE40; break;
188         case 0x300C: nVert = 0xFE41; break;
189         case 0x300D: nVert = 0xFE42; break;
190         case 0x300E: nVert = 0xFE43; break;
191         case 0x300F: nVert = 0xFE44; break;
192 		*/
193     }
194 
195     return nVert;
196 #endif
197 }
198 
199 // -----------------------------------------------------------------------
200 
201 VCL_DLLPUBLIC sal_UCS4 GetMirroredChar( sal_UCS4 nChar )
202 {
203     nChar = u_charMirror( nChar );
204     return nChar;
205 }
206 
207 // -----------------------------------------------------------------------
208 
209 // Get simple approximations for unicodes
210 const char* GetAutofallback( sal_UCS4 nChar )
211 {
212     const char* pStr = NULL;
213     switch( nChar )
214     {
215         case 0x01C0:
216         case 0x2223:
217         case 0x2758:
218             pStr = "|"; break;
219         case 0x02DC:
220             pStr = "~"; break;
221         case 0x037E:
222             pStr = ";"; break;
223         case 0x2000:
224         case 0x2001:
225         case 0x2002:
226         case 0x2003:
227         case 0x2004:
228         case 0x2005:
229         case 0x2006:
230         case 0x2007:
231         case 0x2008:
232         case 0x2009:
233         case 0x200A:
234         case 0x202F:
235             pStr = " "; break;
236         case 0x2010:
237         case 0x2011:
238         case 0x2012:
239         case 0x2013:
240         case 0x2014:
241             pStr = "-"; break;
242         case 0x2015:
243             pStr = "--"; break;
244         case 0x2016:
245             pStr = "||"; break;
246         case 0x2017:
247             pStr = "_"; break;
248         case 0x2018:
249         case 0x2019:
250         case 0x201B:
251             pStr = "\'"; break;
252         case 0x201A:
253             pStr = ","; break;
254         case 0x201C:
255         case 0x201D:
256         case 0x201E:
257         case 0x201F:
258         case 0x2033:
259             pStr = "\""; break;
260         case 0x2039:
261             pStr = "<"; break;
262         case 0x203A:
263             pStr = ">"; break;
264         case 0x203C:
265             pStr = "!!"; break;
266         case 0x203D:
267             pStr = "?"; break;
268         case 0x2044:
269         case 0x2215:
270             pStr = "/"; break;
271         case 0x2048:
272             pStr = "?!"; break;
273         case 0x2049:
274             pStr = "!?"; break;
275         case 0x2216:
276             pStr = "\\"; break;
277         case 0x2217:
278             pStr = "*"; break;
279         case 0x2236:
280             pStr = ":"; break;
281         case 0x2264:
282             pStr = "<="; break;
283         case 0x2265:
284             pStr = "<="; break;
285         case 0x2303:
286             pStr = "^"; break;
287     }
288 
289     return pStr;
290 }
291 
292 // -----------------------------------------------------------------------
293 
294 sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
295 {
296     // currently only conversion from ASCII digits is interesting
297     if( (nChar < '0') || ('9' < nChar) )
298         return nChar;
299 
300     int nOffset;
301     // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
302     // CAVEAT! To some like Mongolian MS assigned the same primary language
303     // although the script type is different!
304     switch( eLang & LANGUAGE_MASK_PRIMARY )
305     {
306         default:
307             nOffset = 0;
308             break;
309         case LANGUAGE_ARABIC_SAUDI_ARABIA  & LANGUAGE_MASK_PRIMARY:
310             nOffset = 0x0660 - '0';  // arabic-indic digits
311             break;
312         case LANGUAGE_FARSI         & LANGUAGE_MASK_PRIMARY:
313         case LANGUAGE_URDU          & LANGUAGE_MASK_PRIMARY:
314         case LANGUAGE_PUNJABI       & LANGUAGE_MASK_PRIMARY: //???
315         case LANGUAGE_SINDHI        & LANGUAGE_MASK_PRIMARY:
316             nOffset = 0x06F0 - '0';  // eastern arabic-indic digits
317             break;
318         case LANGUAGE_BENGALI       & LANGUAGE_MASK_PRIMARY:
319             nOffset = 0x09E6 - '0';  // bengali
320             break;
321         case LANGUAGE_HINDI         & LANGUAGE_MASK_PRIMARY:
322             nOffset = 0x0966 - '0';  // devanagari
323             break;
324         case LANGUAGE_AMHARIC_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
325         case LANGUAGE_TIGRIGNA_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
326         // TODO case:
327             nOffset = 0x1369 - '0';  // ethiopic
328             break;
329         case LANGUAGE_GUJARATI      & LANGUAGE_MASK_PRIMARY:
330             nOffset = 0x0AE6 - '0';  // gujarati
331             break;
332 #ifdef LANGUAGE_GURMUKHI // TODO case:
333         case LANGUAGE_GURMUKHI      & LANGUAGE_MASK_PRIMARY:
334             nOffset = 0x0A66 - '0';  // gurmukhi
335             break;
336 #endif
337         case LANGUAGE_KANNADA       & LANGUAGE_MASK_PRIMARY:
338             nOffset = 0x0CE6 - '0';  // kannada
339             break;
340         case LANGUAGE_KHMER         & LANGUAGE_MASK_PRIMARY:
341             nOffset = 0x17E0 - '0';  // khmer
342             break;
343         case LANGUAGE_LAO           & LANGUAGE_MASK_PRIMARY:
344             nOffset = 0x0ED0 - '0';  // lao
345             break;
346         case LANGUAGE_MALAYALAM     & LANGUAGE_MASK_PRIMARY:
347             nOffset = 0x0D66 - '0';  // malayalam
348             break;
349         case LANGUAGE_MONGOLIAN     & LANGUAGE_MASK_PRIMARY:
350             if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
351                 nOffset = 0x1810 - '0';   // mongolian
352             else
353                 nOffset = 0;              // mongolian cyrillic
354             break;
355         case LANGUAGE_BURMESE       & LANGUAGE_MASK_PRIMARY:
356             nOffset = 0x1040 - '0';  // myanmar
357             break;
358         case LANGUAGE_ORIYA         & LANGUAGE_MASK_PRIMARY:
359             nOffset = 0x0B66 - '0';  // oriya
360             break;
361         case LANGUAGE_TAMIL         & LANGUAGE_MASK_PRIMARY:
362             nOffset = 0x0BE7 - '0';  // tamil
363             break;
364         case LANGUAGE_TELUGU        & LANGUAGE_MASK_PRIMARY:
365             nOffset = 0x0C66 - '0';  // telugu
366             break;
367         case LANGUAGE_THAI          & LANGUAGE_MASK_PRIMARY:
368             nOffset = 0x0E50 - '0';  // thai
369             break;
370         case LANGUAGE_TIBETAN       & LANGUAGE_MASK_PRIMARY:
371             nOffset = 0x0F20 - '0';  // tibetan
372             break;
373 #if 0 // TODO: use language type for these digit substitutions?
374         // TODO case:
375             nOffset = 0x2776 - '0';  // dingbat circled
376             break;
377         // TODO case:
378             nOffset = 0x2070 - '0';  // superscript
379             break;
380         // TODO case:
381             nOffset = 0x2080 - '0';  // subscript
382             break;
383 #endif
384     }
385 
386     nChar += nOffset;
387     return nChar;
388 }
389 
390 // -----------------------------------------------------------------------
391 
392 inline bool IsControlChar( sal_UCS4 cChar )
393 {
394     // C0 control characters
395     if( (0x0001 <= cChar) && (cChar <= 0x001F) )
396         return true;
397     // formatting characters
398     if( (0x200E <= cChar) && (cChar <= 0x200F) )
399         return true;
400     if( (0x2028 <= cChar) && (cChar <= 0x202E) )
401         return true;
402     // deprecated formatting characters
403     if( (0x206A <= cChar) && (cChar <= 0x206F) )
404         return true;
405     if( (0x2060 == cChar) )
406         return true;
407     // byte order markers and invalid unicode
408     if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
409         return true;
410     return false;
411 }
412 
413 // =======================================================================
414 
415 bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
416 {
417     // check if charpos could extend current run
418     int nIndex = maRuns.size();
419     if( nIndex >= 2 )
420     {
421         int nRunPos0 = maRuns[ nIndex-2 ];
422         int nRunPos1 = maRuns[ nIndex-1 ];
423         if( ((nCharPos + bRTL) == nRunPos1)
424 	&&  ((nRunPos0 > nRunPos1) == bRTL) )
425         {
426             // extend current run by new charpos
427             maRuns[ nIndex-1 ] = nCharPos + !bRTL;
428             return false;
429         }
430         // ignore new charpos when it is in current run
431         if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
432             return false;
433         if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
434             return false;
435     }
436 
437     // else append a new run consisting of the new charpos
438     maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
439     maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
440     return true;
441 }
442 
443 // -----------------------------------------------------------------------
444 
445 bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
446 {
447     if( nCharPos0 == nCharPos1 )
448         return false;
449 
450     // swap if needed
451     if( bRTL == (nCharPos0 < nCharPos1) )
452     {
453         int nTemp = nCharPos0;
454         nCharPos0 = nCharPos1;
455         nCharPos1 = nTemp;
456     }
457 
458     // append new run
459     maRuns.push_back( nCharPos0 );
460     maRuns.push_back( nCharPos1 );
461     return true;
462 }
463 
464 // -----------------------------------------------------------------------
465 
466 bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
467 {
468     if( mnRunIndex >= (int)maRuns.size() )
469         return false;
470 
471     int nMinCharPos = maRuns[ mnRunIndex+0 ];
472     int nEndCharPos = maRuns[ mnRunIndex+1 ];
473     if( nMinCharPos > nEndCharPos ) // reversed in RTL case
474     {
475         int nTemp = nMinCharPos;
476         nMinCharPos = nEndCharPos;
477         nEndCharPos = nTemp;
478     }
479 
480     if( nCharPos < nMinCharPos )
481         return false;
482     if( nCharPos >= nEndCharPos )
483         return false;
484     return true;
485 }
486 
487 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
488 {
489     bool bRet = false;
490     int nRunIndex = mnRunIndex;
491 
492     ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
493 
494     pThis->ResetPos();
495 
496     for (size_t i = 0; i < maRuns.size(); i+=2)
497     {
498         if( (bRet = PosIsInRun( nCharPos )) == true )
499             break;
500         pThis->NextRun();
501     }
502 
503     pThis->mnRunIndex = nRunIndex;
504     return bRet;
505 }
506 
507 
508 // -----------------------------------------------------------------------
509 
510 bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
511 {
512     // negative nCharPos => reset to first run
513     if( *nCharPos < 0 )
514         mnRunIndex = 0;
515 
516     // return false when all runs completed
517     if( mnRunIndex >= (int)maRuns.size() )
518         return false;
519 
520     int nRunPos0 = maRuns[ mnRunIndex+0 ];
521     int nRunPos1 = maRuns[ mnRunIndex+1 ];
522     *bRightToLeft = (nRunPos0 > nRunPos1);
523 
524     if( *nCharPos < 0 )
525     {
526         // get first valid nCharPos in run
527         *nCharPos = nRunPos0;
528     }
529     else
530     {
531         // advance to next nCharPos for LTR case
532         if( !*bRightToLeft )
533             ++(*nCharPos);
534 
535         // advance to next run if current run is completed
536         if( *nCharPos == nRunPos1 )
537         {
538             if( (mnRunIndex += 2) >= (int)maRuns.size() )
539                 return false;
540             nRunPos0 = maRuns[ mnRunIndex+0 ];
541             nRunPos1 = maRuns[ mnRunIndex+1 ];
542             *bRightToLeft = (nRunPos0 > nRunPos1);
543             *nCharPos = nRunPos0;
544         }
545     }
546 
547     // advance to next nCharPos for RTL case
548     if( *bRightToLeft )
549         --(*nCharPos);
550 
551     return true;
552 }
553 
554 // -----------------------------------------------------------------------
555 
556 bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
557 {
558     if( mnRunIndex >= (int)maRuns.size() )
559         return false;
560 
561     int nRunPos0 = maRuns[ mnRunIndex+0 ];
562     int nRunPos1 = maRuns[ mnRunIndex+1 ];
563     *bRightToLeft = (nRunPos1 < nRunPos0) ;
564     if( !*bRightToLeft )
565     {
566         *nMinRunPos = nRunPos0;
567         *nEndRunPos = nRunPos1;
568     }
569     else
570     {
571         *nMinRunPos = nRunPos1;
572         *nEndRunPos = nRunPos0;
573     }
574     return true;
575 }
576 
577 // =======================================================================
578 
579 ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen,
580     int nMinCharPos, int nEndCharPos, int nFlags )
581 :
582     mnFlags( nFlags ),
583     mnLength( nLen ),
584     mnMinCharPos( nMinCharPos ),
585     mnEndCharPos( nEndCharPos ),
586     mpStr( pStr ),
587     mpDXArray( NULL ),
588     mnLayoutWidth( 0 ),
589     mnOrientation( 0 )
590 {
591     if( mnFlags & SAL_LAYOUT_BIDI_STRONG )
592     {
593         // handle strong BiDi mode
594 
595         // do not bother to BiDi analyze strong LTR/RTL
596         // TODO: can we assume these strings do not have unicode control chars?
597         //       if not remove the control characters from the runs
598         bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
599         AddRun( mnMinCharPos, mnEndCharPos, bRTL );
600     }
601     else
602     {
603         // handle weak BiDi mode
604 
605         UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
606         if( mnFlags & SAL_LAYOUT_BIDI_RTL )
607             nLevel = UBIDI_DEFAULT_RTL;
608 
609         // prepare substring for BiDi analysis
610         // TODO: reuse allocated pParaBidi
611         UErrorCode rcI18n = U_ZERO_ERROR;
612         UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
613         if( !pParaBidi )
614             return;
615         ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n );	// UChar != sal_Unicode in MinGW
616 
617         UBiDi* pLineBidi = pParaBidi;
618         int nSubLength = mnEndCharPos - mnMinCharPos;
619         if( nSubLength != mnLength )
620         {
621             pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
622             ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
623         }
624 
625         // run BiDi algorithm
626         const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
627         //maRuns.resize( 2 * nRunCount );
628         for( int i = 0; i < nRunCount; ++i )
629         {
630             int32_t nMinPos, nLength;
631             const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
632             const int nPos0 = nMinPos + mnMinCharPos;
633             const int nPos1 = nPos0 + nLength;
634 
635             const bool bRTL = (nDir == UBIDI_RTL);
636             AddRun( nPos0, nPos1, bRTL );
637         }
638 
639         // cleanup BiDi engine
640         if( pLineBidi != pParaBidi )
641             ubidi_close( pLineBidi );
642         ubidi_close( pParaBidi );
643     }
644 
645     // prepare calls to GetNextPos/GetNextRun
646     maRuns.ResetPos();
647 }
648 
649 // -----------------------------------------------------------------------
650 
651 // add a run after splitting it up to get rid of control chars
652 void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
653 {
654     DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
655 
656     // remove control characters from runs by splitting them up
657     if( !bRTL )
658     {
659         for( int i = nCharPos0; i < nCharPos1; ++i )
660             if( IsControlChar( mpStr[i] ) )
661             {
662                 // add run until control char
663                 maRuns.AddRun( nCharPos0, i, bRTL );
664                 nCharPos0 = i + 1;
665             }
666     }
667     else
668     {
669         for( int i = nCharPos1; --i >= nCharPos0; )
670             if( IsControlChar( mpStr[i] ) )
671             {
672                 // add run until control char
673                 maRuns.AddRun( i+1, nCharPos1, bRTL );
674                 nCharPos1 = i;
675             }
676     }
677 
678     // add remainder of run
679     maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
680 }
681 
682 // -----------------------------------------------------------------------
683 
684 bool ImplLayoutArgs::PrepareFallback()
685 {
686     // short circuit if no fallback is needed
687     if( maReruns.IsEmpty() )
688     {
689         maRuns.Clear();
690         return false;
691     }
692 
693     // convert the fallback requests to layout requests
694     bool bRTL;
695     int nMin, nEnd;
696 
697     // get the individual fallback requests
698     typedef std::vector<int> IntVector;
699     IntVector aPosVector;
700     aPosVector.reserve( mnLength );
701     maReruns.ResetPos();
702     for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() )
703         for( int i = nMin; i < nEnd; ++i )
704             aPosVector.push_back( i );
705     maReruns.Clear();
706 
707     // sort the individual fallback requests
708     std::sort( aPosVector.begin(), aPosVector.end() );
709 
710     // adjust fallback runs to have the same order and limits of the original runs
711     ImplLayoutRuns aNewRuns;
712     maRuns.ResetPos();
713     for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
714     {
715         if( !bRTL) {
716             IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
717             for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
718                 aNewRuns.AddPos( *it, bRTL );
719         } else {
720             IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
721             while( (it != aPosVector.begin()) && (*--it >= nMin) )
722                 aNewRuns.AddPos( *it, bRTL );
723         }
724     }
725 
726     maRuns = aNewRuns;  // TODO: use vector<>::swap()
727     maRuns.ResetPos();
728     return true;
729 }
730 
731 // -----------------------------------------------------------------------
732 
733 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
734 {
735     bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
736     maRuns.NextRun();
737     return bValid;
738 }
739 
740 // =======================================================================
741 
742 SalLayout::SalLayout()
743 :   mnMinCharPos( -1 ),
744     mnEndCharPos( -1 ),
745     mnLayoutFlags( 0 ),
746     mnUnitsPerPixel( 1 ),
747     mnOrientation( 0 ),
748     mnRefCount( 1 ),
749     maDrawOffset( 0, 0 )
750 {}
751 
752 // -----------------------------------------------------------------------
753 
754 SalLayout::~SalLayout()
755 {}
756 
757 // -----------------------------------------------------------------------
758 
759 void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
760 {
761     mnMinCharPos  = rArgs.mnMinCharPos;
762     mnEndCharPos  = rArgs.mnEndCharPos;
763     mnLayoutFlags = rArgs.mnFlags;
764     mnOrientation = rArgs.mnOrientation;
765 }
766 
767 // -----------------------------------------------------------------------
768 
769 void SalLayout::Reference() const
770 {
771     // TODO: protect when multiple threads can access this
772     ++mnRefCount;
773 }
774 
775 // -----------------------------------------------------------------------
776 
777 void SalLayout::Release() const
778 {
779     // TODO: protect when multiple threads can access this
780     if( --mnRefCount > 0 )
781         return;
782     // const_cast because some compilers violate ANSI C++ spec
783     delete const_cast<SalLayout*>(this);
784 }
785 
786 // -----------------------------------------------------------------------
787 
788 Point SalLayout::GetDrawPosition( const Point& rRelative ) const
789 {
790     Point aPos = maDrawBase;
791     Point aOfs = rRelative + maDrawOffset;
792 
793     if( mnOrientation == 0 )
794         aPos += aOfs;
795     else
796     {
797         // cache trigonometric results
798         static int nOldOrientation = 0;
799         static double fCos = 1.0, fSin = 0.0;
800         if( nOldOrientation != mnOrientation )
801         {
802             nOldOrientation = mnOrientation;
803             double fRad = mnOrientation * (M_PI / 1800.0);
804             fCos = cos( fRad );
805             fSin = sin( fRad );
806         }
807 
808         double fX = aOfs.X();
809         double fY = aOfs.Y();
810         long nX = static_cast<long>( +fCos * fX + fSin * fY );
811         long nY = static_cast<long>( +fCos * fY - fSin * fX );
812         aPos += Point( nX, nY );
813     }
814 
815     return aPos;
816 }
817 
818 // -----------------------------------------------------------------------
819 
820 // returns asian kerning values in quarter of character width units
821 // to enable automatic halfwidth substitution for fullwidth punctuation
822 // return value is negative for l, positive for r, zero for neutral
823 
824 // If the range doesn't match in 0x3000 and 0x30FB, please change
825 // also ImplCalcKerning.
826 
827 int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
828 {
829     // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
830     static signed char nTable[0x30] =
831     {
832          0, -2, -2,  0,   0,  0,  0,  0,  +2, -2, +2, -2,  +2, -2, +2, -2,
833         +2, -2,  0,  0,  +2, -2, +2, -2,   0,  0,  0,  0,   0, +2, -2, -2,
834          0,  0,  0,  0,   0,  0,  0,  0,   0,  0, -2, -2,  +2, +2, -2, -2
835     };
836 
837     int nResult = 0;
838     if( (c >= 0x3000) && (c < 0x3030) )
839         nResult = nTable[ c - 0x3000 ];
840     else switch( c )
841     {
842 #if 0 // TODO: enable it for real-fixed-width fonts?
843         case ':': case ';': case '!':
844             if( !bVertical )
845                 nResult = bLeft ? -1 : +1;  // 25% left and right
846             break;
847 #endif
848         case 0x30FB:
849             nResult = bLeft ? -1 : +1;      // 25% left/right/top/bottom
850             break;
851         case 0x2019: case 0x201D:
852         case 0xFF01: case 0xFF09: case 0xFF0C:
853         case 0xFF1A: case 0xFF1B:
854             nResult = -2;
855             break;
856         case 0x2018: case 0x201C:
857         case 0xFF08:
858             nResult = +2;
859             break;
860         default:
861             break;
862     }
863 
864     return nResult;
865 }
866 
867 // -----------------------------------------------------------------------
868 
869 bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
870     ::basegfx::B2DPolyPolygonVector& rVector ) const
871 {
872     bool bAllOk = true;
873     bool bOneOk = false;
874 
875     Point aPos;
876     ::basegfx::B2DPolyPolygon aGlyphOutline;
877     for( int nStart = 0;;)
878     {
879         sal_GlyphId nLGlyph;
880         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
881             break;
882 
883         // get outline of individual glyph, ignoring "empty" glyphs
884         bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
885         bAllOk &= bSuccess;
886         bOneOk |= bSuccess;
887         // only add non-empty outlines
888         if( bSuccess && (aGlyphOutline.count() > 0) )
889         {
890             if( aPos.X() || aPos.Y() )
891             {
892 	    	    aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
893     	    }
894 
895             // insert outline at correct position
896             rVector.push_back( aGlyphOutline );
897         }
898     }
899 
900     return (bAllOk & bOneOk);
901 }
902 
903 // -----------------------------------------------------------------------
904 
905 bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
906 {
907     bool bRet = false;
908     rRect.SetEmpty();
909 
910     Point aPos;
911     Rectangle aRectangle;
912     for( int nStart = 0;;)
913     {
914         sal_GlyphId nLGlyph;
915         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
916             break;
917 
918         // get bounding rectangle of individual glyph
919         if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
920         {
921             // merge rectangle
922             aRectangle += aPos;
923             rRect.Union( aRectangle );
924             bRet = true;
925         }
926     }
927 
928     return bRet;
929 }
930 
931 // -----------------------------------------------------------------------
932 
933 bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const
934 {
935     bool bRet = false;
936     if( nGlyph & GF_ISCHAR )
937     {
938         long nChar = nGlyph & GF_IDXMASK;
939         bRet = (nChar <= 0x0020)                    // blank
940             //|| (nChar == 0x00A0)                  // non breaking space
941             || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
942             || (nChar == 0x3000);                   // ideographic space
943     }
944     else
945         bRet = ((nGlyph & GF_IDXMASK) == 3);
946     return bRet;
947 }
948 
949 // -----------------------------------------------------------------------
950 
951 const ImplFontData* SalLayout::GetFallbackFontData( sal_GlyphId /*nGlyphId*/ ) const
952 {
953 #if 0
954     int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT
955     assert( nFallbackLevel == 0 );
956 #endif
957     return NULL;
958 }
959 
960 // =======================================================================
961 
962 GenericSalLayout::GenericSalLayout()
963 :   mpGlyphItems(0),
964     mnGlyphCount(0),
965     mnGlyphCapacity(0)
966 {}
967 
968 // -----------------------------------------------------------------------
969 
970 GenericSalLayout::~GenericSalLayout()
971 {
972     delete[] mpGlyphItems;
973 }
974 
975 // -----------------------------------------------------------------------
976 
977 void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
978 {
979     // TODO: use std::list<GlyphItem>
980     if( mnGlyphCount >= mnGlyphCapacity )
981     {
982         mnGlyphCapacity += 16 + 3 * mnGlyphCount;
983         GlyphItem* pNewGI = new GlyphItem[ mnGlyphCapacity ];
984         if( mpGlyphItems )
985         {
986             for( int i = 0; i < mnGlyphCount; ++i )
987                 pNewGI[ i ] = mpGlyphItems[ i ];
988             delete[] mpGlyphItems;
989         }
990         mpGlyphItems = pNewGI;
991     }
992 
993     mpGlyphItems[ mnGlyphCount++ ] = rGlyphItem;
994 }
995 
996 // -----------------------------------------------------------------------
997 
998 bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
999 {
1000     // initialize character extents buffer
1001     int nCharCount = mnEndCharPos - mnMinCharPos;
1002     for( int n = 0; n < nCharCount; ++n )
1003         pCharWidths[n] = 0;
1004 
1005     // determine cluster extents
1006     const GlyphItem* const pEnd = mpGlyphItems + mnGlyphCount;
1007     for( const GlyphItem* pG = mpGlyphItems; pG < pEnd; ++pG )
1008     {
1009         // use cluster start to get char index
1010         if( !pG->IsClusterStart() )
1011             continue;
1012 
1013         int n = pG->mnCharPos;
1014         if( n >= mnEndCharPos )
1015             continue;
1016         n -= mnMinCharPos;
1017         if( n < 0 )
1018             continue;
1019 
1020         // left glyph in cluster defines default extent
1021         long nXPosMin = pG->maLinearPos.X();
1022         long nXPosMax = nXPosMin + pG->mnNewWidth;
1023 
1024         // calculate right x-position for this glyph cluster
1025         // break if no more glyphs in layout
1026         // break at next glyph cluster start
1027         while( (pG+1 < pEnd) && !pG[1].IsClusterStart() )
1028         {
1029             // advance to next glyph in cluster
1030             ++pG;
1031 
1032 			if( pG->IsDiacritic() )
1033 				continue; // ignore diacritics
1034             // get leftmost x-extent of this glyph
1035             long nXPos = pG->maLinearPos.X();
1036             if( nXPosMin > nXPos )
1037                 nXPosMin = nXPos;
1038 
1039             // get rightmost x-extent of this glyph
1040             nXPos += pG->mnNewWidth;
1041             if( nXPosMax < nXPos )
1042                 nXPosMax = nXPos;
1043         }
1044 
1045         // when the current cluster overlaps with the next one assume
1046         // rightmost cluster edge is the leftmost edge of next cluster
1047 		// for clusters that do not have x-sorted glyphs
1048 		// TODO: avoid recalculation of left bound in next cluster iteration
1049 		for( const GlyphItem* pN = pG; ++pN < pEnd; )
1050 		{
1051 			if( pN->IsClusterStart() )
1052 				break;
1053 			if( pN->IsDiacritic() )
1054 				continue;	// ignore diacritics
1055 			if( nXPosMax > pN->maLinearPos.X() )
1056 				nXPosMax = pN->maLinearPos.X();
1057 		}
1058 		if( nXPosMax < nXPosMin )
1059 			nXPosMin = nXPosMax = 0;
1060 
1061         // character width is sum of glyph cluster widths
1062         pCharWidths[n] += nXPosMax - nXPosMin;
1063     }
1064 
1065     // TODO: distribute the cluster width proportionally to the characters
1066     // clusters (e.g. ligatures) correspond to more than one char index,
1067     // so some character widths are still uninitialized. This is solved
1068     // by setting the first charwidth of the cluster to the cluster width
1069 
1070     return true;
1071 }
1072 
1073 // -----------------------------------------------------------------------
1074 
1075 long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
1076 {
1077     if( pCharWidths )
1078         if( !GetCharWidths( pCharWidths ) )
1079             return 0;
1080 
1081     long nWidth = GetTextWidth();
1082     return nWidth;
1083 }
1084 
1085 // -----------------------------------------------------------------------
1086 
1087 // the text width is the maximum logical extent of all glyphs
1088 long GenericSalLayout::GetTextWidth() const
1089 {
1090     if( mnGlyphCount <= 0 )
1091         return 0;
1092 
1093     // initialize the extent
1094     long nMinPos = 0;
1095     long nMaxPos = 0;
1096 
1097     const GlyphItem* pG = mpGlyphItems;
1098     for( int i = mnGlyphCount; --i >= 0; ++pG )
1099     {
1100         // update the text extent with the glyph extent
1101         long nXPos = pG->maLinearPos.X();
1102         if( nMinPos > nXPos )
1103             nMinPos = nXPos;
1104         nXPos += pG->mnNewWidth;
1105         if( nMaxPos < nXPos )
1106             nMaxPos = nXPos;
1107     }
1108 
1109     long nWidth = nMaxPos - nMinPos;
1110     return nWidth;
1111 }
1112 
1113 // -----------------------------------------------------------------------
1114 
1115 void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1116 {
1117     SalLayout::AdjustLayout( rArgs );
1118 
1119     if( rArgs.mpDXArray )
1120         ApplyDXArray( rArgs );
1121     else if( rArgs.mnLayoutWidth )
1122         Justify( rArgs.mnLayoutWidth );
1123 }
1124 
1125 // -----------------------------------------------------------------------
1126 
1127 void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
1128 {
1129     if( mnGlyphCount <= 0 )
1130         return;
1131 
1132     // determine cluster boundaries and x base offset
1133     const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1134     int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
1135     int i, n;
1136     long nBasePointX = -1;
1137     if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
1138         nBasePointX = 0;
1139     for( i = 0; i < nCharCount; ++i )
1140         pLogCluster[ i ] = -1;
1141     GlyphItem* pG = mpGlyphItems;
1142     for( i = 0; i < mnGlyphCount; ++i, ++pG )
1143     {
1144         n = pG->mnCharPos - rArgs.mnMinCharPos;
1145         if( (n < 0) || (nCharCount <= n) )
1146             continue;
1147         if( pLogCluster[ n ] < 0 )
1148             pLogCluster[ n ] = i;
1149         if( nBasePointX < 0 )
1150             nBasePointX = pG->maLinearPos.X();
1151     }
1152 	// retarget unresolved pLogCluster[n] to a glyph inside the cluster
1153 	// TODO: better do it while the deleted-glyph markers are still there
1154 	for( n = 0; n < nCharCount; ++n )
1155 		if( (i = pLogCluster[0]) >= 0 )
1156 			break;
1157 	if( n >= nCharCount )
1158 		return;
1159 	for( n = 0; n < nCharCount; ++n )
1160 	{
1161 		if( pLogCluster[ n ] < 0 )
1162 			pLogCluster[ n ] = i;
1163 		else
1164 			i = pLogCluster[ n ];
1165 	}
1166 
1167     // calculate adjusted cluster widths
1168     sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( mnGlyphCount * sizeof(long) );
1169     for( i = 0; i < mnGlyphCount; ++i )
1170         pNewGlyphWidths[ i ] = 0;
1171 
1172     bool bRTL;
1173     for( int nCharPos = i = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
1174     {
1175         n = nCharPos - rArgs.mnMinCharPos;
1176         if( (n < 0) || (nCharCount <= n) )  continue;
1177 
1178         if( pLogCluster[ n ] >= 0 )
1179             i = pLogCluster[ n ];
1180         if( i >= 0 )
1181         {
1182             long nDelta = rArgs.mpDXArray[ n ] ;
1183             if( n > 0 )
1184                 nDelta -= rArgs.mpDXArray[ n-1 ];
1185             pNewGlyphWidths[ i ] += nDelta * mnUnitsPerPixel;
1186         }
1187     }
1188 
1189     // move cluster positions using the adjusted widths
1190     long nDelta = 0;
1191     long nNewPos = 0;
1192     pG = mpGlyphItems;
1193     for( i = 0; i < mnGlyphCount; ++i, ++pG )
1194     {
1195         if( pG->IsClusterStart() )
1196         {
1197             // calculate original and adjusted cluster width
1198             int nOldClusterWidth = pG->mnNewWidth;
1199             int nNewClusterWidth = pNewGlyphWidths[i];
1200             GlyphItem* pClusterG = pG + 1;
1201             for( int j = i; ++j < mnGlyphCount; ++pClusterG )
1202             {
1203                 if( pClusterG->IsClusterStart() )
1204                     break;
1205                 if( !pClusterG->IsDiacritic() ) // #i99367# ignore diacritics
1206                 	nOldClusterWidth += pClusterG->mnNewWidth;
1207                 nNewClusterWidth += pNewGlyphWidths[j];
1208             }
1209             const int nDiff = nNewClusterWidth - nOldClusterWidth;
1210 
1211             // adjust cluster glyph widths and positions
1212             nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X());
1213             if( !pG->IsRTLGlyph() )
1214             {
1215                 // for LTR case extend rightmost glyph in cluster
1216                 pClusterG[-1].mnNewWidth += nDiff;
1217             }
1218             else
1219             {
1220                 // right align cluster in new space for RTL case
1221                 pG->mnNewWidth += nDiff;
1222                 nDelta += nDiff;
1223             }
1224 
1225             nNewPos += nNewClusterWidth;
1226         }
1227 
1228         pG->maLinearPos.X() += nDelta;
1229     }
1230 }
1231 
1232 // -----------------------------------------------------------------------
1233 
1234 void GenericSalLayout::Justify( long nNewWidth )
1235 {
1236     nNewWidth *= mnUnitsPerPixel;
1237     int nOldWidth = GetTextWidth();
1238     if( !nOldWidth || nNewWidth==nOldWidth )
1239         return;
1240 
1241     // find rightmost glyph, it won't get stretched
1242     GlyphItem* pGRight = mpGlyphItems + mnGlyphCount - 1;
1243 
1244     // count stretchable glyphs
1245     GlyphItem* pG;
1246     int nStretchable = 0;
1247     int nMaxGlyphWidth = 0;
1248     for( pG = mpGlyphItems; pG < pGRight; ++pG )
1249     {
1250         if( !pG->IsDiacritic() )
1251             ++nStretchable;
1252         if( nMaxGlyphWidth < pG->mnOrigWidth )
1253 	        nMaxGlyphWidth = pG->mnOrigWidth;
1254     }
1255 
1256     // move rightmost glyph to requested position
1257     nOldWidth -= pGRight->mnOrigWidth;
1258     if( nOldWidth <= 0 )
1259         return;
1260     if( nNewWidth < nMaxGlyphWidth)
1261         nNewWidth = nMaxGlyphWidth;
1262     nNewWidth -= pGRight->mnOrigWidth;
1263     pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
1264 
1265     // justify glyph widths and positions
1266     int nDiffWidth = nNewWidth - nOldWidth;
1267     if( nDiffWidth >= 0) // expanded case
1268     {
1269         // expand width by distributing space between glyphs evenly
1270         int nDeltaSum = 0;
1271         for( pG = mpGlyphItems; pG < pGRight; ++pG )
1272         {
1273             // move glyph to justified position
1274             pG->maLinearPos.X() += nDeltaSum;
1275 
1276             // do not stretch non-stretchable glyphs
1277             if( pG->IsDiacritic() || (nStretchable <= 0) )
1278                 continue;
1279 
1280             // distribute extra space equally to stretchable glyphs
1281             int nDeltaWidth = nDiffWidth / nStretchable--;
1282             nDiffWidth     -= nDeltaWidth;
1283             pG->mnNewWidth += nDeltaWidth;
1284             nDeltaSum      += nDeltaWidth;
1285         }
1286     }
1287     else // condensed case
1288     {
1289         // squeeze width by moving glyphs proportionally
1290         double fSqueeze = (double)nNewWidth / nOldWidth;
1291         for( pG = mpGlyphItems; ++pG < pGRight;)
1292         {
1293             int nX = pG->maLinearPos.X() - maBasePoint.X();
1294             nX = (int)(nX * fSqueeze);
1295             pG->maLinearPos.X() = nX + maBasePoint.X();
1296         }
1297         // adjust glyph widths to new positions
1298         for( pG = mpGlyphItems; pG < pGRight; ++pG )
1299             pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X();
1300     }
1301 }
1302 
1303 // -----------------------------------------------------------------------
1304 
1305 void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
1306 {
1307     long nOffset = 0;
1308 
1309     GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
1310     for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
1311     {
1312         const int n = pG->mnCharPos;
1313         if( n < nLength - 1)
1314         {
1315             // ignore code ranges that are not affected by asian punctuation compression
1316             const sal_Unicode cHere = pStr[n];
1317             if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
1318                 continue;
1319             const sal_Unicode cNext = pStr[n+1];
1320             if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
1321                 continue;
1322 
1323             // calculate compression values
1324             const bool bVertical = false;
1325             long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
1326             long nKernNext  = -CalcAsianKerning( cNext, false, bVertical );
1327 
1328             // apply punctuation compression to logical glyph widths
1329             long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
1330             if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
1331             {
1332                 int nGlyphWidth = pG->mnOrigWidth;
1333                 nDelta = (nDelta * nGlyphWidth + 2) / 4;
1334                 if( pG+1 == pGEnd )
1335                     pG->mnNewWidth += nDelta;
1336                 nOffset += nDelta;
1337             }
1338         }
1339 
1340         // adjust the glyph positions to the new glyph widths
1341         if( pG+1 != pGEnd )
1342             pG->maLinearPos.X() += nOffset;
1343     }
1344 }
1345 
1346 // -----------------------------------------------------------------------
1347 
1348 void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
1349 {
1350     // TODO: reimplement method when container type for GlyphItems changes
1351 
1352     // skip if the kashida glyph in the font looks suspicious
1353     if( nKashidaWidth <= 0 )
1354         return;
1355 
1356     // calculate max number of needed kashidas
1357     const GlyphItem* pG1 = mpGlyphItems;
1358     int nKashidaCount = 0, i;
1359     for( i = 0; i < mnGlyphCount; ++i, ++pG1 )
1360     {
1361         // only inject kashidas in RTL contexts
1362         if( !pG1->IsRTLGlyph() )
1363             continue;
1364         // no kashida-injection for blank justified expansion either
1365         if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
1366             continue;
1367 
1368         // calculate gap, ignore if too small
1369         const int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
1370         // worst case is one kashida even for mini-gaps
1371         if( 3 * nGapWidth >= nKashidaWidth )
1372             nKashidaCount += 1 + (nGapWidth / nKashidaWidth);
1373     }
1374 
1375     if( !nKashidaCount )
1376         return;
1377 
1378     // reallocate glyph array for additional kashidas
1379     // TODO: reuse array if additional glyphs would fit
1380     mnGlyphCapacity = mnGlyphCount + nKashidaCount;
1381     GlyphItem* pNewGlyphItems = new GlyphItem[ mnGlyphCapacity ];
1382     GlyphItem* pG2 = pNewGlyphItems;
1383     pG1 = mpGlyphItems;
1384     for( i = mnGlyphCount; --i >= 0; ++pG1, ++pG2 )
1385     {
1386         // default action is to copy array element
1387         *pG2 = *pG1;
1388 
1389         // only inject kashida in RTL contexts
1390         if( !pG1->IsRTLGlyph() )
1391             continue;
1392         // no kashida-injection for blank justified expansion either
1393         if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
1394             continue;
1395 
1396         // calculate gap, skip if too small
1397         int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
1398         if( 3*nGapWidth < nKashidaWidth )
1399             continue;
1400 
1401         // fill gap with kashidas
1402         nKashidaCount = 0;
1403         Point aPos = pG1->maLinearPos;
1404         aPos.X() -= nGapWidth; // cluster is already right aligned
1405         for(; nGapWidth > 0; nGapWidth -= nKashidaWidth, ++nKashidaCount )
1406         {
1407             *(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos,
1408                 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth );
1409             aPos.X() += nKashidaWidth;
1410         }
1411 
1412         // fixup rightmost kashida for gap remainder
1413         if( nGapWidth < 0 )
1414         {
1415             aPos.X() += nGapWidth;
1416             if( nKashidaCount <= 1 )
1417                 nGapWidth /= 2;               // for small gap move kashida to middle
1418             pG2[-1].mnNewWidth += nGapWidth;  // adjust kashida width to gap width
1419             pG2[-1].maLinearPos.X() += nGapWidth;
1420         }
1421 
1422         // when kashidas were inserted move the original cluster
1423         // to the right and shrink it to it's original width
1424         *pG2 = *pG1;
1425         pG2->maLinearPos.X() = aPos.X();
1426         pG2->mnNewWidth = pG2->mnOrigWidth;
1427      }
1428 
1429     // use the new glyph array
1430     DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" );
1431     delete[] mpGlyphItems;
1432     mpGlyphItems = pNewGlyphItems;
1433     mnGlyphCount = pG2 - pNewGlyphItems;
1434 }
1435 
1436 // -----------------------------------------------------------------------
1437 
1438 void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
1439 {
1440     // initialize result array
1441     long nXPos = -1;
1442     int i;
1443     for( i = 0; i < nMaxIndex; ++i )
1444         pCaretXArray[ i ] = nXPos;
1445 
1446     // calculate caret positions using glyph array
1447     const GlyphItem* pG = mpGlyphItems;
1448     for( i = mnGlyphCount; --i >= 0; ++pG )
1449     {
1450         nXPos = pG->maLinearPos.X();
1451         long nXRight = nXPos + pG->mnOrigWidth;
1452         int n = pG->mnCharPos;
1453         int nCurrIdx = 2 * (n - mnMinCharPos);
1454         if( !pG->IsRTLGlyph() )
1455         {
1456             // normal positions for LTR case
1457             pCaretXArray[ nCurrIdx ]   = nXPos;
1458             pCaretXArray[ nCurrIdx+1 ] = nXRight;
1459         }
1460         else
1461         {
1462             // reverse positions for RTL case
1463             pCaretXArray[ nCurrIdx ]   = nXRight;
1464             pCaretXArray[ nCurrIdx+1 ] = nXPos;
1465         }
1466     }
1467 }
1468 
1469 // -----------------------------------------------------------------------
1470 
1471 int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
1472 {
1473     int nCharCapacity = mnEndCharPos - mnMinCharPos;
1474     sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
1475     if( !GetCharWidths( pCharWidths ) )
1476         return STRING_LEN;
1477 
1478     long nWidth = 0;
1479     for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1480     {
1481         nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
1482         if( nWidth >= nMaxWidth )
1483             return i;
1484         nWidth += nCharExtra;
1485     }
1486 
1487     return STRING_LEN;
1488 }
1489 
1490 // -----------------------------------------------------------------------
1491 
1492 int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1493     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
1494 {
1495     const GlyphItem* pG = mpGlyphItems + nStart;
1496 
1497     // find next glyph in substring
1498     for(; nStart < mnGlyphCount; ++nStart, ++pG )
1499     {
1500         int n = pG->mnCharPos;
1501         if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
1502             break;
1503     }
1504 
1505     // return zero if no more glyph found
1506     if( nStart >= mnGlyphCount )
1507         return 0;
1508 
1509     // calculate absolute position in pixel units
1510     Point aRelativePos = pG->maLinearPos - maBasePoint;
1511 
1512     // find more glyphs which can be merged into one drawing instruction
1513     int nCount = 0;
1514     long nYPos = pG->maLinearPos.Y();
1515     long nOldFlags = pG->mnGlyphIndex;
1516     for(;;)
1517     {
1518         // update return data with glyph info
1519         ++nCount;
1520         *(pGlyphs++) = pG->mnGlyphIndex;
1521         if( pCharPosAry )
1522             *(pCharPosAry++) = pG->mnCharPos;
1523         if( pGlyphAdvAry )
1524             *pGlyphAdvAry = pG->mnNewWidth;
1525 
1526         // break at end of glyph list
1527         if( ++nStart >= mnGlyphCount )
1528             break;
1529         // break when enough glyphs
1530         if( nCount >= nLen )
1531             break;
1532 
1533         long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X();
1534         if( pGlyphAdvAry )
1535         {
1536             // override default advance width with correct value
1537             *(pGlyphAdvAry++) = nGlyphAdvance;
1538         }
1539         else
1540         {
1541             // stop when next x-position is unexpected
1542             if( pG->mnOrigWidth != nGlyphAdvance )
1543                 break;
1544         }
1545 
1546         // advance to next glyph
1547         ++pG;
1548 
1549         // stop when next y-position is unexpected
1550         if( nYPos != pG->maLinearPos.Y() )
1551             break;
1552 
1553         // stop when no longer in string
1554         int n = pG->mnCharPos;
1555         if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
1556             break;
1557 
1558         // stop when glyph flags change
1559         if( (nOldFlags ^ pG->mnGlyphIndex) & GF_FLAGMASK )
1560             break;
1561 
1562         nOldFlags = pG->mnGlyphIndex; // &GF_FLAGMASK not needed for test above
1563     }
1564 
1565     aRelativePos.X() /= mnUnitsPerPixel;
1566     aRelativePos.Y() /= mnUnitsPerPixel;
1567     rPos = GetDrawPosition( aRelativePos );
1568 
1569     return nCount;
1570 }
1571 
1572 // -----------------------------------------------------------------------
1573 
1574 void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
1575 {
1576     if( nStart >= mnGlyphCount )
1577         return;
1578 
1579     GlyphItem* pG = mpGlyphItems + nStart;
1580     // the nNewXPos argument determines the new cell position
1581     // as RTL-glyphs are right justified in their cell
1582     // the cell position needs to be adjusted to the glyph position
1583     if( pG->IsRTLGlyph() )
1584         nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
1585     // calculate the x-offset to the old position
1586     long nXDelta = nNewXPos - pG->maLinearPos.X();
1587     // adjust all following glyph positions if needed
1588     if( nXDelta != 0 )
1589     {
1590         GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
1591         for(; pG < pGEnd; ++pG )
1592             pG->maLinearPos.X() += nXDelta;
1593     }
1594 }
1595 
1596 // -----------------------------------------------------------------------
1597 
1598 void GenericSalLayout::DropGlyph( int nStart )
1599 {
1600     if( nStart >= mnGlyphCount )
1601         return;
1602     GlyphItem* pG = mpGlyphItems + nStart;
1603     pG->mnGlyphIndex = GF_DROPPED;
1604     pG->mnCharPos = -1;
1605 }
1606 
1607 // -----------------------------------------------------------------------
1608 
1609 void GenericSalLayout::Simplify( bool bIsBase )
1610 {
1611     const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
1612 
1613     // remove dropped glyphs inplace
1614     GlyphItem* pGDst = mpGlyphItems;
1615     const GlyphItem* pGSrc = mpGlyphItems;
1616     const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
1617     for(; pGSrc < pGEnd; ++pGSrc )
1618     {
1619         if( pGSrc->mnGlyphIndex == nDropMarker )
1620             continue;
1621         if( pGDst != pGSrc )
1622             *pGDst = *pGSrc;
1623         ++pGDst;
1624     }
1625     mnGlyphCount = pGDst - mpGlyphItems;
1626 }
1627 
1628 // -----------------------------------------------------------------------
1629 
1630 // make sure GlyphItems are sorted left to right
1631 void GenericSalLayout::SortGlyphItems()
1632 {
1633     // move cluster components behind their cluster start (especially for RTL)
1634     // using insertion sort because the glyph items are "almost sorted"
1635     const GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
1636     for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
1637     {
1638         // find a cluster starting with a diacritic
1639 		if( !pG->IsDiacritic() )
1640 			continue;
1641 		if( !pG->IsClusterStart() )
1642 			continue;
1643         for( GlyphItem* pBaseGlyph = pG; ++pBaseGlyph < pGEnd; )
1644         {
1645 	        // find the base glyph matching to the misplaced diacritic
1646            	if( pBaseGlyph->IsClusterStart() )
1647            		break;
1648            	if( pBaseGlyph->IsDiacritic() )
1649            		continue;
1650 
1651             // found the matching base glyph
1652             // => this base glyph becomes the new cluster start
1653             const GlyphItem aDiacritic = *pG;
1654             *pG = *pBaseGlyph;
1655             *pBaseGlyph = aDiacritic;
1656 
1657 			// update glyph flags of swapped glyphitems
1658             pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
1659             pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
1660 			// prepare for checking next cluster
1661 			pG = pBaseGlyph;
1662             break;
1663         }
1664     }
1665 }
1666 
1667 // =======================================================================
1668 
1669 MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const ImplFontData* pBaseFont )
1670 :   SalLayout()
1671 ,   mnLevel( 1 )
1672 ,   mbInComplete( false )
1673 {
1674     //maFallbackRuns[0].Clear();
1675     mpFallbackFonts[ 0 ] = pBaseFont;
1676     mpLayouts[ 0 ]  = &rBaseLayout;
1677     mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
1678 }
1679 
1680 void MultiSalLayout::SetInComplete(bool bInComplete)
1681 {
1682     mbInComplete = bInComplete;
1683     maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
1684 }
1685 
1686 // -----------------------------------------------------------------------
1687 
1688 MultiSalLayout::~MultiSalLayout()
1689 {
1690     for( int i = 0; i < mnLevel; ++i )
1691         mpLayouts[ i ]->Release();
1692 }
1693 
1694 // -----------------------------------------------------------------------
1695 
1696 bool MultiSalLayout::AddFallback( SalLayout& rFallback,
1697     ImplLayoutRuns& rFallbackRuns, const ImplFontData* pFallbackFont )
1698 {
1699     if( mnLevel >= MAX_FALLBACK )
1700         return false;
1701 
1702     mpFallbackFonts[ mnLevel ]  = pFallbackFont;
1703     mpLayouts[ mnLevel ]        = &rFallback;
1704     maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1705     ++mnLevel;
1706     return true;
1707 }
1708 
1709 // -----------------------------------------------------------------------
1710 
1711 bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
1712 {
1713     if( mnLevel <= 1 )
1714         return false;
1715     if (!mbInComplete)
1716         maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1717     return true;
1718 }
1719 
1720 // -----------------------------------------------------------------------
1721 
1722 void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1723 {
1724     SalLayout::AdjustLayout( rArgs );
1725     ImplLayoutArgs aMultiArgs = rArgs;
1726 
1727     if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1728     {
1729         // for stretched text in a MultiSalLayout the target width needs to be
1730         // distributed by individually adjusting its virtual character widths
1731         long nTargetWidth = aMultiArgs.mnLayoutWidth;
1732         nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1733         aMultiArgs.mnLayoutWidth = 0;
1734 
1735         // we need to get the original unmodified layouts ready
1736         for( int n = 0; n < mnLevel; ++n )
1737             mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1738         // then we can measure the unmodified metrics
1739         int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1740         sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
1741         FillDXArray( pJustificationArray );
1742         // #i17359# multilayout is not simplified yet, so calculating the
1743         // unjustified width needs handholding; also count the number of
1744         // stretchable virtual char widths
1745         long nOrigWidth = 0;
1746         int nStretchable = 0;
1747         for( int i = 0; i < nCharCount; ++i )
1748         {
1749             // convert array from widths to sum of widths
1750             nOrigWidth += pJustificationArray[i];
1751             if( pJustificationArray[i] > 0 )
1752                 ++nStretchable;
1753         }
1754 
1755         // now we are able to distribute the extra width over the virtual char widths
1756         if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1757         {
1758             int nDiffWidth = nTargetWidth - nOrigWidth;
1759             int nWidthSum = 0;
1760             for( int i = 0; i < nCharCount; ++i )
1761             {
1762                 int nJustWidth = pJustificationArray[i];
1763                 if( (nJustWidth > 0) && (nStretchable > 0) )
1764                 {
1765                     int nDeltaWidth = nDiffWidth / nStretchable;
1766                     nJustWidth += nDeltaWidth;
1767                     nDiffWidth -= nDeltaWidth;
1768                     --nStretchable;
1769                 }
1770                 nWidthSum += nJustWidth;
1771                 pJustificationArray[i] = nWidthSum;
1772             }
1773             if( nWidthSum != nTargetWidth )
1774                 pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1775 
1776             // the justification array is still in base level units
1777             // => convert it to pixel units
1778             if( mnUnitsPerPixel > 1 )
1779             {
1780                 for( int i = 0; i < nCharCount; ++i )
1781                 {
1782                     sal_Int32 nVal = pJustificationArray[ i ];
1783                     nVal += (mnUnitsPerPixel + 1) / 2;
1784                     pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1785                 }
1786             }
1787 
1788             // change the mpDXArray temporarilly (just for the justification)
1789             aMultiArgs.mpDXArray = pJustificationArray;
1790         }
1791     }
1792 
1793     // Compute rtl flags, since in some scripts glyphs/char order can be
1794     // reversed for a few character sequencies e.g. Myanmar
1795     std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1796     rArgs.ResetPos();
1797     bool bRtl;
1798     int nRunStart, nRunEnd;
1799     while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1800     {
1801         if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1802                             vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1803     }
1804     rArgs.ResetPos();
1805 
1806     // prepare "merge sort"
1807     int nStartOld[ MAX_FALLBACK ];
1808     int nStartNew[ MAX_FALLBACK ];
1809     int nCharPos[ MAX_FALLBACK ];
1810     sal_Int32 nGlyphAdv[ MAX_FALLBACK ];
1811     int nValid[ MAX_FALLBACK ] = {0};
1812 
1813     sal_GlyphId nDummy;
1814     Point aPos;
1815     int nLevel = 0, n;
1816     for( n = 0; n < mnLevel; ++n )
1817     {
1818         // now adjust the individual components
1819         if( n > 0 )
1820         {
1821             aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1822             aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
1823         }
1824         mpLayouts[n]->AdjustLayout( aMultiArgs );
1825 
1826         // disable glyph-injection for glyph-fallback SalLayout iteration
1827         mpLayouts[n]->DisableGlyphInjection( true );
1828 
1829         // remove unused parts of component
1830         if( n > 0 )
1831         {
1832             if (mbInComplete && (n == mnLevel-1))
1833                 mpLayouts[n]->Simplify( true );
1834             else
1835                 mpLayouts[n]->Simplify( false );
1836         }
1837 
1838         // prepare merging components
1839         nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1840         nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1841             nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] );
1842 #ifdef MULTI_SL_DEBUG
1843         if (nValid[nLevel]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", n, nStartOld[nLevel], nStartNew[nLevel], aPos.X(), nGlyphAdv[nLevel], nCharPos[nLevel],
1844             rArgs.mpStr[nCharPos[nLevel]]);
1845 #endif
1846         if( (n > 0) && !nValid[ nLevel ] )
1847         {
1848             // an empty fallback layout can be released
1849             mpLayouts[n]->Release();
1850         }
1851         else
1852         {
1853             // reshuffle used fallbacks if needed
1854             if( nLevel != n )
1855             {
1856                 mpLayouts[ nLevel ]         = mpLayouts[ n ];
1857                 mpFallbackFonts[ nLevel ]   = mpFallbackFonts[ n ];
1858                 maFallbackRuns[ nLevel ]    = maFallbackRuns[ n ];
1859             }
1860             ++nLevel;
1861         }
1862     }
1863     mnLevel = nLevel;
1864 
1865     // merge the fallback levels
1866     long nXPos = 0;
1867     double fUnitMul = 1.0;
1868     for( n = 0; n < nLevel; ++n )
1869         maFallbackRuns[n].ResetPos();
1870     int nActiveCharPos = nCharPos[0];
1871     int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
1872         rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1873     int nRunVisibleEndChar = nCharPos[0];
1874     while( nValid[0] && (nLevel > 0))
1875     {
1876         // find best fallback level
1877         for( n = 0; n < nLevel; ++n )
1878             if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1879                 // fallback level n wins when it requested no further fallback
1880                 break;
1881         int nFBLevel = n;
1882 
1883         if( n < nLevel )
1884         {
1885             // use base(n==0) or fallback(n>=1) level
1886             fUnitMul = mnUnitsPerPixel;
1887             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1888             long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1889             mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1890         }
1891         else
1892         {
1893             n = 0;  // keep NotDef in base level
1894             fUnitMul = 1.0;
1895         }
1896 
1897         if( n > 0 )
1898         {
1899             // drop the NotDef glyphs in the base layout run if a fallback run exists
1900             while (
1901                     (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
1902                     (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
1903                   )
1904             {
1905                 mpLayouts[0]->DropGlyph( nStartOld[0] );
1906                 nStartOld[0] = nStartNew[0];
1907                 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
1908                     nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
1909 #ifdef MULTI_SL_DEBUG
1910                 if (nValid[0]) fprintf(mslLog(), "layout[0]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", nStartOld[0], nStartNew[0], aPos.X(), nGlyphAdv[0], nCharPos[0], rArgs.mpStr[nCharPos[0]]);
1911 #endif
1912                 if( !nValid[0] )
1913                    break;
1914             }
1915         }
1916 
1917         // skip to end of layout run and calculate its advance width
1918         int nRunAdvance = 0;
1919         bool bKeepNotDef = (nFBLevel >= nLevel);
1920         for(;;)
1921         {
1922             nRunAdvance += nGlyphAdv[n];
1923 
1924             // proceed to next glyph
1925             nStartOld[n] = nStartNew[n];
1926             int nOrigCharPos = nCharPos[n];
1927             nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1928                 nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
1929 #ifdef MULTI_SL_DEBUG
1930             if (nValid[n]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d a%d c%d %x\n", n, nStartOld[n], nStartNew[n], nGlyphAdv[n], nCharPos[n], rArgs.mpStr[nCharPos[n]]);
1931 #endif
1932             // break after last glyph of active layout
1933             if( !nValid[n] )
1934             {
1935                 // performance optimization (when a fallback layout is no longer needed)
1936                 if( n >= nLevel-1 )
1937                     --nLevel;
1938                 break;
1939             }
1940 
1941             //If the next character is one which belongs to the next level, then we
1942             //are finished here for now, and we'll pick up after the next level has
1943             //been processed
1944             if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
1945             {
1946                 if (nOrigCharPos < nCharPos[n])
1947                 {
1948                     if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
1949                         break;
1950                 }
1951                 else if (nOrigCharPos > nCharPos[n])
1952                 {
1953                     if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
1954                         break;
1955                 }
1956             }
1957 
1958             // break at end of layout run
1959             if( n > 0 )
1960             {
1961                 // skip until end of fallback run
1962                 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
1963                     break;
1964             }
1965             else
1966             {
1967                 // break when a fallback is needed and available
1968                 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
1969                 if( bNeedFallback )
1970                     if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
1971                         break;
1972                 // break when change from resolved to unresolved base layout run
1973                 if( bKeepNotDef && !bNeedFallback )
1974                     { maFallbackRuns[0].NextRun(); break; }
1975                 bKeepNotDef = bNeedFallback;
1976             }
1977             // check for reordered glyphs
1978             if (aMultiArgs.mpDXArray &&
1979                 nRunVisibleEndChar < mnEndCharPos &&
1980                 nRunVisibleEndChar >= mnMinCharPos &&
1981                 nCharPos[n] < mnEndCharPos &&
1982                 nCharPos[n] >= mnMinCharPos)
1983             {
1984                 if (vRtl[nActiveCharPos - mnMinCharPos])
1985                 {
1986                     if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1987                         >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1988                     {
1989                         nRunVisibleEndChar = nCharPos[n];
1990                     }
1991                 }
1992                 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1993                          <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1994                 {
1995                     nRunVisibleEndChar = nCharPos[n];
1996                 }
1997             }
1998         }
1999 
2000         // if a justification array is available
2001         // => use it directly to calculate the corresponding run width
2002         if( aMultiArgs.mpDXArray )
2003         {
2004             // the run advance is the width from the first char
2005             // in the run to the first char in the next run
2006             nRunAdvance = 0;
2007 #ifdef MULTI_SL_DEBUG
2008             const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
2009             int nOldRunAdv = 0;
2010             int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
2011             if( nDXIndex >= 0 )
2012                 nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
2013             nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
2014             if( nDXIndex >= 0 )
2015                 nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
2016             if( !bLTR )
2017                 nOldRunAdv = -nOldRunAdv;
2018 #endif
2019             if (vRtl[nActiveCharPos - mnMinCharPos])
2020             {
2021               if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
2022                   nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
2023               if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
2024                   nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
2025 #ifdef MULTI_SL_DEBUG
2026               fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
2027 #endif
2028             }
2029             else
2030             {
2031                 if (nRunVisibleEndChar >= mnMinCharPos)
2032                   nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
2033                 if (nLastRunEndChar >= mnMinCharPos)
2034                   nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
2035 #ifdef MULTI_SL_DEBUG
2036                 fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
2037 #endif
2038             }
2039             nLastRunEndChar = nRunVisibleEndChar;
2040             nRunVisibleEndChar = nCharPos[0];
2041             // the requested width is still in pixel units
2042             // => convert it to base level font units
2043             nRunAdvance *= mnUnitsPerPixel;
2044         }
2045         else
2046         {
2047             // the measured width is still in fallback font units
2048             // => convert it to base level font units
2049             if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
2050                 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
2051         }
2052 
2053         // calculate new x position (in base level units)
2054         nXPos += nRunAdvance;
2055 
2056         // prepare for next fallback run
2057         nActiveCharPos = nCharPos[0];
2058         // it essential that the runs don't get ahead of themselves and in the
2059         // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
2060         // have already been reached on the base level
2061         for( int i = nFBLevel; --i >= 0;)
2062         {
2063             if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
2064             {
2065                 if (bRtl)
2066                 {
2067                     if (nRunStart > nActiveCharPos)
2068                         maFallbackRuns[i].NextRun();
2069                 }
2070                 else
2071                 {
2072                     if (nRunEnd <= nActiveCharPos)
2073                         maFallbackRuns[i].NextRun();
2074                 }
2075             }
2076         }
2077 //            if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) )
2078 //                maFallbackRuns[i].NextRun();
2079     }
2080 
2081     mpLayouts[0]->Simplify( true );
2082 
2083     // reenable glyph-injection
2084     for( n = 0; n < mnLevel; ++n )
2085         mpLayouts[n]->DisableGlyphInjection( false );
2086 }
2087 
2088 // -----------------------------------------------------------------------
2089 
2090 void MultiSalLayout::InitFont() const
2091 {
2092     if( mnLevel > 0 )
2093         mpLayouts[0]->InitFont();
2094 }
2095 
2096 // -----------------------------------------------------------------------
2097 
2098 const ImplFontData* MultiSalLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const
2099 {
2100     int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
2101     return mpFallbackFonts[ nFallbackLevel ];
2102 }
2103 
2104 // -----------------------------------------------------------------------
2105 
2106 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
2107 {
2108     for( int i = mnLevel; --i >= 0; )
2109     {
2110         SalLayout& rLayout = *mpLayouts[ i ];
2111         rLayout.DrawBase() += maDrawBase;
2112         rLayout.DrawOffset() += maDrawOffset;
2113         rLayout.InitFont();
2114         rLayout.DrawText( rGraphics );
2115         rLayout.DrawOffset() -= maDrawOffset;
2116         rLayout.DrawBase() -= maDrawBase;
2117     }
2118     // NOTE: now the baselevel font is active again
2119 }
2120 
2121  // -----------------------------------------------------------------------
2122 
2123 int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2124 {
2125     if( mnLevel <= 0 )
2126         return STRING_LEN;
2127     if( mnLevel == 1 )
2128         return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
2129 
2130     int nCharCount = mnEndCharPos - mnMinCharPos;
2131     sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
2132     mpLayouts[0]->FillDXArray( pCharWidths );
2133 
2134     for( int n = 1; n < mnLevel; ++n )
2135     {
2136         SalLayout& rLayout = *mpLayouts[ n ];
2137         rLayout.FillDXArray( pCharWidths + nCharCount );
2138         double fUnitMul = mnUnitsPerPixel;
2139         fUnitMul /= rLayout.GetUnitsPerPixel();
2140         for( int i = 0; i < nCharCount; ++i )
2141         {
2142             long w = pCharWidths[ i + nCharCount ];
2143             w = static_cast<long>(w*fUnitMul + 0.5);
2144             pCharWidths[ i ] += w;
2145         }
2146     }
2147 
2148     long nWidth = 0;
2149     for( int i = 0; i < nCharCount; ++i )
2150     {
2151         nWidth += pCharWidths[ i ] * nFactor;
2152         if( nWidth > nMaxWidth )
2153             return (i + mnMinCharPos);
2154         nWidth += nCharExtra;
2155     }
2156 
2157     return STRING_LEN;
2158 }
2159 
2160 // -----------------------------------------------------------------------
2161 
2162 long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
2163 {
2164     long nMaxWidth = 0;
2165 
2166     // prepare merging of fallback levels
2167     sal_Int32* pTempWidths = NULL;
2168     const int nCharCount = mnEndCharPos - mnMinCharPos;
2169     if( pCharWidths )
2170     {
2171         for( int i = 0; i < nCharCount; ++i )
2172             pCharWidths[i] = 0;
2173         pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
2174     }
2175 
2176     for( int n = mnLevel; --n >= 0; )
2177     {
2178         // query every fallback level
2179         long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
2180         if( !nTextWidth )
2181             continue;
2182         // merge results from current level
2183         double fUnitMul = mnUnitsPerPixel;
2184         fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2185         nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
2186         if( nMaxWidth < nTextWidth )
2187             nMaxWidth = nTextWidth;
2188         if( !pCharWidths )
2189             continue;
2190         // calculate virtual char widths using most probable fallback layout
2191         for( int i = 0; i < nCharCount; ++i )
2192         {
2193             // #i17359# restriction:
2194             // one char cannot be resolved from different fallbacks
2195             if( pCharWidths[i] != 0 )
2196                 continue;
2197             long nCharWidth = pTempWidths[i];
2198             if( !nCharWidth )
2199                 continue;
2200             nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
2201             pCharWidths[i] = nCharWidth;
2202         }
2203     }
2204 
2205     return nMaxWidth;
2206 }
2207 
2208 // -----------------------------------------------------------------------
2209 
2210 void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
2211 {
2212     SalLayout& rLayout = *mpLayouts[ 0 ];
2213     rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
2214 
2215     if( mnLevel > 1 )
2216     {
2217         sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
2218         for( int n = 1; n < mnLevel; ++n )
2219         {
2220             mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
2221             double fUnitMul = mnUnitsPerPixel;
2222             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2223             for( int i = 0; i < nMaxIndex; ++i )
2224                 if( pTempPos[i] >= 0 )
2225                 {
2226                     long w = pTempPos[i];
2227                     w = static_cast<long>(w*fUnitMul + 0.5);
2228                     pCaretXArray[i] = w;
2229                 }
2230         }
2231     }
2232 }
2233 
2234 // -----------------------------------------------------------------------
2235 
2236 int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
2237     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
2238 {
2239     // for multi-level fallback only single glyphs should be used
2240     if( mnLevel > 1 && nLen > 1 )
2241         nLen = 1;
2242 
2243     // NOTE: nStart is tagged with current font index
2244     int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
2245     nStart &= ~GF_FONTMASK;
2246     for(; nLevel < mnLevel; ++nLevel, nStart=0 )
2247     {
2248         SalLayout& rLayout = *mpLayouts[ nLevel ];
2249         rLayout.InitFont();
2250         int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
2251             nStart, pGlyphAdvAry, pCharPosAry );
2252         if( nRetVal )
2253         {
2254             int nFontTag = nLevel << GF_FONTSHIFT;
2255             nStart |= nFontTag;
2256             double fUnitMul = mnUnitsPerPixel;
2257             fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
2258             for( int i = 0; i < nRetVal; ++i )
2259             {
2260                 if( pGlyphAdvAry )
2261                 {
2262                     long w = pGlyphAdvAry[i];
2263                     w = static_cast<long>(w * fUnitMul + 0.5);
2264                     pGlyphAdvAry[i] = w;
2265                 }
2266                 pGlyphIdxAry[ i ] |= nFontTag;
2267             }
2268             rPos += maDrawBase;
2269             rPos += maDrawOffset;
2270             return nRetVal;
2271         }
2272     }
2273 
2274     // #111016# reset to base level font when done
2275     mpLayouts[0]->InitFont();
2276     return 0;
2277 }
2278 
2279 // -----------------------------------------------------------------------
2280 
2281 bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
2282     ::basegfx::B2DPolyPolygonVector& rPPV ) const
2283 {
2284     bool bRet = false;
2285 
2286     for( int i = mnLevel; --i >= 0; )
2287     {
2288         SalLayout& rLayout = *mpLayouts[ i ];
2289         rLayout.DrawBase() = maDrawBase;
2290         rLayout.DrawOffset() += maDrawOffset;
2291         rLayout.InitFont();
2292         bRet |= rLayout.GetOutline( rGraphics, rPPV );
2293         rLayout.DrawOffset() -= maDrawOffset;
2294     }
2295 
2296     return bRet;
2297 }
2298 
2299 // -----------------------------------------------------------------------
2300 
2301 bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
2302 {
2303     bool bRet = false;
2304 
2305     Rectangle aRectangle;
2306     for( int i = mnLevel; --i >= 0; )
2307     {
2308         SalLayout& rLayout = *mpLayouts[ i ];
2309         rLayout.DrawBase() = maDrawBase;
2310         rLayout.DrawOffset() += maDrawOffset;
2311         rLayout.InitFont();
2312         if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
2313         {
2314             rRect.Union( aRectangle );
2315             bRet = true;
2316         }
2317         rLayout.DrawOffset() -= maDrawOffset;
2318     }
2319 
2320     return bRet;
2321 }
2322 
2323 // =======================================================================
2324