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