xref: /aoo41x/main/vcl/source/gdi/sallayout.cxx (revision 9f62ea84)
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     return false;
407 }
408 
409 // =======================================================================
410 
411 bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
412 {
413     // check if charpos could extend current run
414     int nIndex = maRuns.size();
415     if( nIndex >= 2 )
416     {
417         int nRunPos0 = maRuns[ nIndex-2 ];
418         int nRunPos1 = maRuns[ nIndex-1 ];
419         if( ((nCharPos + bRTL) == nRunPos1)
420 	&&  ((nRunPos0 > nRunPos1) == bRTL) )
421         {
422             // extend current run by new charpos
423             maRuns[ nIndex-1 ] = nCharPos + !bRTL;
424             return false;
425         }
426         // ignore new charpos when it is in current run
427         if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
428             return false;
429         if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
430             return false;
431     }
432 
433     // else append a new run consisting of the new charpos
434     maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
435     maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
436     return true;
437 }
438 
439 // -----------------------------------------------------------------------
440 
441 bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
442 {
443     if( nCharPos0 == nCharPos1 )
444         return false;
445 
446     // swap if needed
447     if( bRTL == (nCharPos0 < nCharPos1) )
448     {
449         int nTemp = nCharPos0;
450         nCharPos0 = nCharPos1;
451         nCharPos1 = nTemp;
452     }
453 
454     // append new run
455     maRuns.push_back( nCharPos0 );
456     maRuns.push_back( nCharPos1 );
457     return true;
458 }
459 
460 // -----------------------------------------------------------------------
461 
462 bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
463 {
464     if( mnRunIndex >= (int)maRuns.size() )
465         return false;
466 
467     int nMinCharPos = maRuns[ mnRunIndex+0 ];
468     int nEndCharPos = maRuns[ mnRunIndex+1 ];
469     if( nMinCharPos > nEndCharPos ) // reversed in RTL case
470     {
471         int nTemp = nMinCharPos;
472         nMinCharPos = nEndCharPos;
473         nEndCharPos = nTemp;
474     }
475 
476     if( nCharPos < nMinCharPos )
477         return false;
478     if( nCharPos >= nEndCharPos )
479         return false;
480     return true;
481 }
482 
483 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
484 {
485     bool bRet = false;
486     int nRunIndex = mnRunIndex;
487 
488     ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
489 
490     pThis->ResetPos();
491 
492     for (size_t i = 0; i < maRuns.size(); i+=2)
493     {
494         if( (bRet = PosIsInRun( nCharPos )) == true )
495             break;
496         pThis->NextRun();
497     }
498 
499     pThis->mnRunIndex = nRunIndex;
500     return bRet;
501 }
502 
503 
504 // -----------------------------------------------------------------------
505 
506 bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
507 {
508     // negative nCharPos => reset to first run
509     if( *nCharPos < 0 )
510         mnRunIndex = 0;
511 
512     // return false when all runs completed
513     if( mnRunIndex >= (int)maRuns.size() )
514         return false;
515 
516     int nRunPos0 = maRuns[ mnRunIndex+0 ];
517     int nRunPos1 = maRuns[ mnRunIndex+1 ];
518     *bRightToLeft = (nRunPos0 > nRunPos1);
519 
520     if( *nCharPos < 0 )
521     {
522         // get first valid nCharPos in run
523         *nCharPos = nRunPos0;
524     }
525     else
526     {
527         // advance to next nCharPos for LTR case
528         if( !*bRightToLeft )
529             ++(*nCharPos);
530 
531         // advance to next run if current run is completed
532         if( *nCharPos == nRunPos1 )
533         {
534             if( (mnRunIndex += 2) >= (int)maRuns.size() )
535                 return false;
536             nRunPos0 = maRuns[ mnRunIndex+0 ];
537             nRunPos1 = maRuns[ mnRunIndex+1 ];
538             *bRightToLeft = (nRunPos0 > nRunPos1);
539             *nCharPos = nRunPos0;
540         }
541     }
542 
543     // advance to next nCharPos for RTL case
544     if( *bRightToLeft )
545         --(*nCharPos);
546 
547     return true;
548 }
549 
550 // -----------------------------------------------------------------------
551 
552 bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
553 {
554     if( mnRunIndex >= (int)maRuns.size() )
555         return false;
556 
557     int nRunPos0 = maRuns[ mnRunIndex+0 ];
558     int nRunPos1 = maRuns[ mnRunIndex+1 ];
559     *bRightToLeft = (nRunPos1 < nRunPos0) ;
560     if( !*bRightToLeft )
561     {
562         *nMinRunPos = nRunPos0;
563         *nEndRunPos = nRunPos1;
564     }
565     else
566     {
567         *nMinRunPos = nRunPos1;
568         *nEndRunPos = nRunPos0;
569     }
570     return true;
571 }
572 
573 // =======================================================================
574 
575 ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen,
576     int nMinCharPos, int nEndCharPos, int nFlags )
577 :
578     mnFlags( nFlags ),
579     mnLength( nLen ),
580     mnMinCharPos( nMinCharPos ),
581     mnEndCharPos( nEndCharPos ),
582     mpStr( pStr ),
583     mpDXArray( NULL ),
584     mnLayoutWidth( 0 ),
585     mnOrientation( 0 )
586 {
587     if( mnFlags & SAL_LAYOUT_BIDI_STRONG )
588     {
589         // handle strong BiDi mode
590 
591         // do not bother to BiDi analyze strong LTR/RTL
592         // TODO: can we assume these strings do not have unicode control chars?
593         //       if not remove the control characters from the runs
594         bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
595         AddRun( mnMinCharPos, mnEndCharPos, bRTL );
596     }
597     else
598     {
599         // handle weak BiDi mode
600 
601         UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
602         if( mnFlags & SAL_LAYOUT_BIDI_RTL )
603             nLevel = UBIDI_DEFAULT_RTL;
604 
605         // prepare substring for BiDi analysis
606         // TODO: reuse allocated pParaBidi
607         UErrorCode rcI18n = U_ZERO_ERROR;
608         UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
609         if( !pParaBidi )
610             return;
611         ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n );	// UChar != sal_Unicode in MinGW
612 
613         UBiDi* pLineBidi = pParaBidi;
614         int nSubLength = mnEndCharPos - mnMinCharPos;
615         if( nSubLength != mnLength )
616         {
617             pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
618             ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
619         }
620 
621         // run BiDi algorithm
622         const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
623         //maRuns.resize( 2 * nRunCount );
624         for( int i = 0; i < nRunCount; ++i )
625         {
626             int32_t nMinPos, nLength;
627             const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
628             const int nPos0 = nMinPos + mnMinCharPos;
629             const int nPos1 = nPos0 + nLength;
630 
631             const bool bRTL = (nDir == UBIDI_RTL);
632             AddRun( nPos0, nPos1, bRTL );
633         }
634 
635         // cleanup BiDi engine
636         if( pLineBidi != pParaBidi )
637             ubidi_close( pLineBidi );
638         ubidi_close( pParaBidi );
639     }
640 
641     // prepare calls to GetNextPos/GetNextRun
642     maRuns.ResetPos();
643 }
644 
645 // -----------------------------------------------------------------------
646 
647 // add a run after splitting it up to get rid of control chars
648 void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
649 {
650     DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
651 
652     // remove control characters from runs by splitting them up
653     if( !bRTL )
654     {
655         for( int i = nCharPos0; i < nCharPos1; ++i )
656             if( IsControlChar( mpStr[i] ) )
657             {
658                 // add run until control char
659                 maRuns.AddRun( nCharPos0, i, bRTL );
660                 nCharPos0 = i + 1;
661             }
662     }
663     else
664     {
665         for( int i = nCharPos1; --i >= nCharPos0; )
666             if( IsControlChar( mpStr[i] ) )
667             {
668                 // add run until control char
669                 maRuns.AddRun( i+1, nCharPos1, bRTL );
670                 nCharPos1 = i;
671             }
672     }
673 
674     // add remainder of run
675     maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
676 }
677 
678 // -----------------------------------------------------------------------
679 
680 bool ImplLayoutArgs::PrepareFallback()
681 {
682     // short circuit if no fallback is needed
683     if( maReruns.IsEmpty() )
684     {
685         maRuns.Clear();
686         return false;
687     }
688 
689     // convert the fallback requests to layout requests
690     bool bRTL;
691     int nMin, nEnd;
692 
693     // get the individual fallback requests
694     typedef std::vector<int> IntVector;
695     IntVector aPosVector;
696     aPosVector.reserve( mnLength );
697     maReruns.ResetPos();
698     for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() )
699         for( int i = nMin; i < nEnd; ++i )
700             aPosVector.push_back( i );
701     maReruns.Clear();
702 
703     // sort the individual fallback requests
704     std::sort( aPosVector.begin(), aPosVector.end() );
705 
706     // adjust fallback runs to have the same order and limits of the original runs
707     ImplLayoutRuns aNewRuns;
708     maRuns.ResetPos();
709     for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
710     {
711         if( !bRTL) {
712             IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
713             for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
714                 aNewRuns.AddPos( *it, bRTL );
715         } else {
716             IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
717             while( (it != aPosVector.begin()) && (*--it >= nMin) )
718                 aNewRuns.AddPos( *it, bRTL );
719         }
720     }
721 
722     maRuns = aNewRuns;  // TODO: use vector<>::swap()
723     maRuns.ResetPos();
724     return true;
725 }
726 
727 // -----------------------------------------------------------------------
728 
729 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
730 {
731     bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
732     maRuns.NextRun();
733     return bValid;
734 }
735 
736 // =======================================================================
737 
738 SalLayout::SalLayout()
739 :   mnMinCharPos( -1 ),
740     mnEndCharPos( -1 ),
741     mnLayoutFlags( 0 ),
742     mnUnitsPerPixel( 1 ),
743     mnOrientation( 0 ),
744     mnRefCount( 1 ),
745     maDrawOffset( 0, 0 )
746 {}
747 
748 // -----------------------------------------------------------------------
749 
750 SalLayout::~SalLayout()
751 {}
752 
753 // -----------------------------------------------------------------------
754 
755 void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
756 {
757     mnMinCharPos  = rArgs.mnMinCharPos;
758     mnEndCharPos  = rArgs.mnEndCharPos;
759     mnLayoutFlags = rArgs.mnFlags;
760     mnOrientation = rArgs.mnOrientation;
761 }
762 
763 // -----------------------------------------------------------------------
764 
765 void SalLayout::Reference() const
766 {
767     // TODO: protect when multiple threads can access this
768     ++mnRefCount;
769 }
770 
771 // -----------------------------------------------------------------------
772 
773 void SalLayout::Release() const
774 {
775     // TODO: protect when multiple threads can access this
776     if( --mnRefCount > 0 )
777         return;
778     // const_cast because some compilers violate ANSI C++ spec
779     delete const_cast<SalLayout*>(this);
780 }
781 
782 // -----------------------------------------------------------------------
783 
784 Point SalLayout::GetDrawPosition( const Point& rRelative ) const
785 {
786     Point aPos = maDrawBase;
787     Point aOfs = rRelative + maDrawOffset;
788 
789     if( mnOrientation == 0 )
790         aPos += aOfs;
791     else
792     {
793         // cache trigonometric results
794         static int nOldOrientation = 0;
795         static double fCos = 1.0, fSin = 0.0;
796         if( nOldOrientation != mnOrientation )
797         {
798             nOldOrientation = mnOrientation;
799             double fRad = mnOrientation * (M_PI / 1800.0);
800             fCos = cos( fRad );
801             fSin = sin( fRad );
802         }
803 
804         double fX = aOfs.X();
805         double fY = aOfs.Y();
806         long nX = static_cast<long>( +fCos * fX + fSin * fY );
807         long nY = static_cast<long>( +fCos * fY - fSin * fX );
808         aPos += Point( nX, nY );
809     }
810 
811     return aPos;
812 }
813 
814 // -----------------------------------------------------------------------
815 
816 // returns asian kerning values in quarter of character width units
817 // to enable automatic halfwidth substitution for fullwidth punctuation
818 // return value is negative for l, positive for r, zero for neutral
819 
820 // If the range doesn't match in 0x3000 and 0x30FB, please change
821 // also ImplCalcKerning.
822 
823 int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
824 {
825     // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
826     static signed char nTable[0x30] =
827     {
828          0, -2, -2,  0,   0,  0,  0,  0,  +2, -2, +2, -2,  +2, -2, +2, -2,
829         +2, -2,  0,  0,  +2, -2, +2, -2,   0,  0,  0,  0,   0, +2, -2, -2,
830          0,  0,  0,  0,   0,  0,  0,  0,   0,  0, -2, -2,  +2, +2, -2, -2
831     };
832 
833     int nResult = 0;
834     if( (c >= 0x3000) && (c < 0x3030) )
835         nResult = nTable[ c - 0x3000 ];
836     else switch( c )
837     {
838 #if 0 // TODO: enable it for real-fixed-width fonts?
839         case ':': case ';': case '!':
840             if( !bVertical )
841                 nResult = bLeft ? -1 : +1;  // 25% left and right
842             break;
843 #endif
844         case 0x30FB:
845             nResult = bLeft ? -1 : +1;      // 25% left/right/top/bottom
846             break;
847         case 0x2019: case 0x201D:
848         case 0xFF01: case 0xFF09: case 0xFF0C:
849         case 0xFF1A: case 0xFF1B:
850             nResult = -2;
851             break;
852         case 0x2018: case 0x201C:
853         case 0xFF08:
854             nResult = +2;
855             break;
856         default:
857             break;
858     }
859 
860     return nResult;
861 }
862 
863 // -----------------------------------------------------------------------
864 
865 bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
866     ::basegfx::B2DPolyPolygonVector& rVector ) const
867 {
868     bool bAllOk = true;
869     bool bOneOk = false;
870 
871     Point aPos;
872     ::basegfx::B2DPolyPolygon aGlyphOutline;
873     for( int nStart = 0;;)
874     {
875         sal_GlyphId nLGlyph;
876         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
877             break;
878 
879         // get outline of individual glyph, ignoring "empty" glyphs
880         bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
881         bAllOk &= bSuccess;
882         bOneOk |= bSuccess;
883         // only add non-empty outlines
884         if( bSuccess && (aGlyphOutline.count() > 0) )
885         {
886             if( aPos.X() || aPos.Y() )
887             {
888 	    	    aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
889     	    }
890 
891             // insert outline at correct position
892             rVector.push_back( aGlyphOutline );
893         }
894     }
895 
896     return (bAllOk & bOneOk);
897 }
898 
899 // -----------------------------------------------------------------------
900 
901 bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
902 {
903     bool bRet = false;
904     rRect.SetEmpty();
905 
906     Point aPos;
907     Rectangle aRectangle;
908     for( int nStart = 0;;)
909     {
910         sal_GlyphId nLGlyph;
911         if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
912             break;
913 
914         // get bounding rectangle of individual glyph
915         if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
916         {
917             // merge rectangle
918             aRectangle += aPos;
919             rRect.Union( aRectangle );
920             bRet = true;
921         }
922     }
923 
924     return bRet;
925 }
926 
927 // -----------------------------------------------------------------------
928 
929 bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const
930 {
931     bool bRet = false;
932     if( nGlyph & GF_ISCHAR )
933     {
934         long nChar = nGlyph & GF_IDXMASK;
935         bRet = (nChar <= 0x0020)                    // blank
936             //|| (nChar == 0x00A0)                  // non breaking space
937             || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
938             || (nChar == 0x3000);                   // ideographic space
939     }
940     else
941         bRet = ((nGlyph & GF_IDXMASK) == 3);
942     return bRet;
943 }
944 
945 // -----------------------------------------------------------------------
946 
947 const ImplFontData* SalLayout::GetFallbackFontData( sal_GlyphId /*nGlyphId*/ ) const
948 {
949 #if 0
950     int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT
951     assert( nFallbackLevel == 0 );
952 #endif
953     return NULL;
954 }
955 
956 // =======================================================================
957 
958 GenericSalLayout::GenericSalLayout()
959 :   mpGlyphItems(0),
960     mnGlyphCount(0),
961     mnGlyphCapacity(0)
962 {}
963 
964 // -----------------------------------------------------------------------
965 
966 GenericSalLayout::~GenericSalLayout()
967 {
968     delete[] mpGlyphItems;
969 }
970 
971 // -----------------------------------------------------------------------
972 
973 void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
974 {
975     // TODO: use std::list<GlyphItem>
976     if( mnGlyphCount >= mnGlyphCapacity )
977     {
978         mnGlyphCapacity += 16 + 3 * mnGlyphCount;
979         GlyphItem* pNewGI = new GlyphItem[ mnGlyphCapacity ];
980         if( mpGlyphItems )
981         {
982             for( int i = 0; i < mnGlyphCount; ++i )
983                 pNewGI[ i ] = mpGlyphItems[ i ];
984             delete[] mpGlyphItems;
985         }
986         mpGlyphItems = pNewGI;
987     }
988 
989     mpGlyphItems[ mnGlyphCount++ ] = rGlyphItem;
990 }
991 
992 // -----------------------------------------------------------------------
993 
994 bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
995 {
996     // initialize character extents buffer
997     int nCharCount = mnEndCharPos - mnMinCharPos;
998     for( int n = 0; n < nCharCount; ++n )
999         pCharWidths[n] = 0;
1000 
1001     // determine cluster extents
1002     const GlyphItem* const pEnd = mpGlyphItems + mnGlyphCount;
1003     for( const GlyphItem* pG = mpGlyphItems; pG < pEnd; ++pG )
1004     {
1005         // use cluster start to get char index
1006         if( !pG->IsClusterStart() )
1007             continue;
1008 
1009         int n = pG->mnCharPos;
1010         if( n >= mnEndCharPos )
1011             continue;
1012         n -= mnMinCharPos;
1013         if( n < 0 )
1014             continue;
1015 
1016         // left glyph in cluster defines default extent
1017         long nXPosMin = pG->maLinearPos.X();
1018         long nXPosMax = nXPosMin + pG->mnNewWidth;
1019 
1020         // calculate right x-position for this glyph cluster
1021         // break if no more glyphs in layout
1022         // break at next glyph cluster start
1023         while( (pG+1 < pEnd) && !pG[1].IsClusterStart() )
1024         {
1025             // advance to next glyph in cluster
1026             ++pG;
1027 
1028 			if( pG->IsDiacritic() )
1029 				continue; // ignore diacritics
1030             // get leftmost x-extent of this glyph
1031             long nXPos = pG->maLinearPos.X();
1032             if( nXPosMin > nXPos )
1033                 nXPosMin = nXPos;
1034 
1035             // get rightmost x-extent of this glyph
1036             nXPos += pG->mnNewWidth;
1037             if( nXPosMax < nXPos )
1038                 nXPosMax = nXPos;
1039         }
1040 
1041         // when the current cluster overlaps with the next one assume
1042         // rightmost cluster edge is the leftmost edge of next cluster
1043 		// for clusters that do not have x-sorted glyphs
1044 		// TODO: avoid recalculation of left bound in next cluster iteration
1045 		for( const GlyphItem* pN = pG; ++pN < pEnd; )
1046 		{
1047 			if( pN->IsClusterStart() )
1048 				break;
1049 			if( pN->IsDiacritic() )
1050 				continue;	// ignore diacritics
1051 			if( nXPosMax > pN->maLinearPos.X() )
1052 				nXPosMax = pN->maLinearPos.X();
1053 		}
1054 		if( nXPosMax < nXPosMin )
1055 			nXPosMin = nXPosMax = 0;
1056 
1057         // character width is sum of glyph cluster widths
1058         pCharWidths[n] += nXPosMax - nXPosMin;
1059     }
1060 
1061     // TODO: distribute the cluster width proportionally to the characters
1062     // clusters (e.g. ligatures) correspond to more than one char index,
1063     // so some character widths are still uninitialized. This is solved
1064     // by setting the first charwidth of the cluster to the cluster width
1065 
1066     return true;
1067 }
1068 
1069 // -----------------------------------------------------------------------
1070 
1071 long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
1072 {
1073     if( pCharWidths )
1074         if( !GetCharWidths( pCharWidths ) )
1075             return 0;
1076 
1077     long nWidth = GetTextWidth();
1078     return nWidth;
1079 }
1080 
1081 // -----------------------------------------------------------------------
1082 
1083 // the text width is the maximum logical extent of all glyphs
1084 long GenericSalLayout::GetTextWidth() const
1085 {
1086     if( mnGlyphCount <= 0 )
1087         return 0;
1088 
1089     // initialize the extent
1090     long nMinPos = 0;
1091     long nMaxPos = 0;
1092 
1093     const GlyphItem* pG = mpGlyphItems;
1094     for( int i = mnGlyphCount; --i >= 0; ++pG )
1095     {
1096         // update the text extent with the glyph extent
1097         long nXPos = pG->maLinearPos.X();
1098         if( nMinPos > nXPos )
1099             nMinPos = nXPos;
1100         nXPos += pG->mnNewWidth;
1101         if( nMaxPos < nXPos )
1102             nMaxPos = nXPos;
1103     }
1104 
1105     long nWidth = nMaxPos - nMinPos;
1106     return nWidth;
1107 }
1108 
1109 // -----------------------------------------------------------------------
1110 
1111 void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1112 {
1113     SalLayout::AdjustLayout( rArgs );
1114 
1115     if( rArgs.mpDXArray )
1116         ApplyDXArray( rArgs );
1117     else if( rArgs.mnLayoutWidth )
1118         Justify( rArgs.mnLayoutWidth );
1119 }
1120 
1121 // -----------------------------------------------------------------------
1122 
1123 void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
1124 {
1125     if( mnGlyphCount <= 0 )
1126         return;
1127 
1128     // determine cluster boundaries and x base offset
1129     const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1130     int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
1131     int i, n;
1132     long nBasePointX = -1;
1133     if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
1134         nBasePointX = 0;
1135     for( i = 0; i < nCharCount; ++i )
1136         pLogCluster[ i ] = -1;
1137     GlyphItem* pG = mpGlyphItems;
1138     for( i = 0; i < mnGlyphCount; ++i, ++pG )
1139     {
1140         n = pG->mnCharPos - rArgs.mnMinCharPos;
1141         if( (n < 0) || (nCharCount <= n) )
1142             continue;
1143         if( pLogCluster[ n ] < 0 )
1144             pLogCluster[ n ] = i;
1145         if( nBasePointX < 0 )
1146             nBasePointX = pG->maLinearPos.X();
1147     }
1148 	// retarget unresolved pLogCluster[n] to a glyph inside the cluster
1149 	// TODO: better do it while the deleted-glyph markers are still there
1150 	for( n = 0; n < nCharCount; ++n )
1151 		if( (i = pLogCluster[0]) >= 0 )
1152 			break;
1153 	if( n >= nCharCount )
1154 		return;
1155 	for( n = 0; n < nCharCount; ++n )
1156 	{
1157 		if( pLogCluster[ n ] < 0 )
1158 			pLogCluster[ n ] = i;
1159 		else
1160 			i = pLogCluster[ n ];
1161 	}
1162 
1163     // calculate adjusted cluster widths
1164     sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( mnGlyphCount * sizeof(long) );
1165     for( i = 0; i < mnGlyphCount; ++i )
1166         pNewGlyphWidths[ i ] = 0;
1167 
1168     bool bRTL;
1169     for( int nCharPos = i = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
1170     {
1171         n = nCharPos - rArgs.mnMinCharPos;
1172         if( (n < 0) || (nCharCount <= n) )  continue;
1173 
1174         if( pLogCluster[ n ] >= 0 )
1175             i = pLogCluster[ n ];
1176         if( i >= 0 )
1177         {
1178             long nDelta = rArgs.mpDXArray[ n ] ;
1179             if( n > 0 )
1180                 nDelta -= rArgs.mpDXArray[ n-1 ];
1181             pNewGlyphWidths[ i ] += nDelta * mnUnitsPerPixel;
1182         }
1183     }
1184 
1185     // move cluster positions using the adjusted widths
1186     long nDelta = 0;
1187     long nNewPos = 0;
1188     pG = mpGlyphItems;
1189     for( i = 0; i < mnGlyphCount; ++i, ++pG )
1190     {
1191         if( pG->IsClusterStart() )
1192         {
1193             // calculate original and adjusted cluster width
1194             int nOldClusterWidth = pG->mnNewWidth;
1195             int nNewClusterWidth = pNewGlyphWidths[i];
1196             GlyphItem* pClusterG = pG + 1;
1197             for( int j = i; ++j < mnGlyphCount; ++pClusterG )
1198             {
1199                 if( pClusterG->IsClusterStart() )
1200                     break;
1201                 if( !pClusterG->IsDiacritic() ) // #i99367# ignore diacritics
1202                 	nOldClusterWidth += pClusterG->mnNewWidth;
1203                 nNewClusterWidth += pNewGlyphWidths[j];
1204             }
1205             const int nDiff = nNewClusterWidth - nOldClusterWidth;
1206 
1207             // adjust cluster glyph widths and positions
1208             nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X());
1209             if( !pG->IsRTLGlyph() )
1210             {
1211                 // for LTR case extend rightmost glyph in cluster
1212                 pClusterG[-1].mnNewWidth += nDiff;
1213             }
1214             else
1215             {
1216                 // right align cluster in new space for RTL case
1217                 pG->mnNewWidth += nDiff;
1218                 nDelta += nDiff;
1219             }
1220 
1221             nNewPos += nNewClusterWidth;
1222         }
1223 
1224         pG->maLinearPos.X() += nDelta;
1225     }
1226 }
1227 
1228 // -----------------------------------------------------------------------
1229 
1230 void GenericSalLayout::Justify( long nNewWidth )
1231 {
1232     nNewWidth *= mnUnitsPerPixel;
1233     int nOldWidth = GetTextWidth();
1234     if( !nOldWidth || nNewWidth==nOldWidth )
1235         return;
1236 
1237     // find rightmost glyph, it won't get stretched
1238     GlyphItem* pGRight = mpGlyphItems + mnGlyphCount - 1;
1239 
1240     // count stretchable glyphs
1241     GlyphItem* pG;
1242     int nStretchable = 0;
1243     int nMaxGlyphWidth = 0;
1244     for( pG = mpGlyphItems; pG < pGRight; ++pG )
1245     {
1246         if( !pG->IsDiacritic() )
1247             ++nStretchable;
1248         if( nMaxGlyphWidth < pG->mnOrigWidth )
1249 	        nMaxGlyphWidth = pG->mnOrigWidth;
1250     }
1251 
1252     // move rightmost glyph to requested position
1253     nOldWidth -= pGRight->mnOrigWidth;
1254     if( nOldWidth <= 0 )
1255         return;
1256     if( nNewWidth < nMaxGlyphWidth)
1257         nNewWidth = nMaxGlyphWidth;
1258     nNewWidth -= pGRight->mnOrigWidth;
1259     pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
1260 
1261     // justify glyph widths and positions
1262     int nDiffWidth = nNewWidth - nOldWidth;
1263     if( nDiffWidth >= 0) // expanded case
1264     {
1265         // expand width by distributing space between glyphs evenly
1266         int nDeltaSum = 0;
1267         for( pG = mpGlyphItems; pG < pGRight; ++pG )
1268         {
1269             // move glyph to justified position
1270             pG->maLinearPos.X() += nDeltaSum;
1271 
1272             // do not stretch non-stretchable glyphs
1273             if( pG->IsDiacritic() || (nStretchable <= 0) )
1274                 continue;
1275 
1276             // distribute extra space equally to stretchable glyphs
1277             int nDeltaWidth = nDiffWidth / nStretchable--;
1278             nDiffWidth     -= nDeltaWidth;
1279             pG->mnNewWidth += nDeltaWidth;
1280             nDeltaSum      += nDeltaWidth;
1281         }
1282     }
1283     else // condensed case
1284     {
1285         // squeeze width by moving glyphs proportionally
1286         double fSqueeze = (double)nNewWidth / nOldWidth;
1287         for( pG = mpGlyphItems; ++pG < pGRight;)
1288         {
1289             int nX = pG->maLinearPos.X() - maBasePoint.X();
1290             nX = (int)(nX * fSqueeze);
1291             pG->maLinearPos.X() = nX + maBasePoint.X();
1292         }
1293         // adjust glyph widths to new positions
1294         for( pG = mpGlyphItems; pG < pGRight; ++pG )
1295             pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X();
1296     }
1297 }
1298 
1299 // -----------------------------------------------------------------------
1300 
1301 void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
1302 {
1303     long nOffset = 0;
1304 
1305     GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
1306     for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
1307     {
1308         const int n = pG->mnCharPos;
1309         if( n < nLength - 1)
1310         {
1311             // ignore code ranges that are not affected by asian punctuation compression
1312             const sal_Unicode cHere = pStr[n];
1313             if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
1314                 continue;
1315             const sal_Unicode cNext = pStr[n+1];
1316             if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
1317                 continue;
1318 
1319             // calculate compression values
1320             const bool bVertical = false;
1321             long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
1322             long nKernNext  = -CalcAsianKerning( cNext, false, bVertical );
1323 
1324             // apply punctuation compression to logical glyph widths
1325             long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
1326             if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
1327             {
1328                 int nGlyphWidth = pG->mnOrigWidth;
1329                 nDelta = (nDelta * nGlyphWidth + 2) / 4;
1330                 if( pG+1 == pGEnd )
1331                     pG->mnNewWidth += nDelta;
1332                 nOffset += nDelta;
1333             }
1334         }
1335 
1336         // adjust the glyph positions to the new glyph widths
1337         if( pG+1 != pGEnd )
1338             pG->maLinearPos.X() += nOffset;
1339     }
1340 }
1341 
1342 // -----------------------------------------------------------------------
1343 
1344 void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
1345 {
1346     // TODO: reimplement method when container type for GlyphItems changes
1347 
1348     // skip if the kashida glyph in the font looks suspicious
1349     if( nKashidaWidth <= 0 )
1350         return;
1351 
1352     // calculate max number of needed kashidas
1353     const GlyphItem* pG1 = mpGlyphItems;
1354     int nKashidaCount = 0, i;
1355     for( i = 0; i < mnGlyphCount; ++i, ++pG1 )
1356     {
1357         // only inject kashidas in RTL contexts
1358         if( !pG1->IsRTLGlyph() )
1359             continue;
1360         // no kashida-injection for blank justified expansion either
1361         if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
1362             continue;
1363 
1364         // calculate gap, ignore if too small
1365         const int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
1366         // worst case is one kashida even for mini-gaps
1367         if( 3 * nGapWidth >= nKashidaWidth )
1368             nKashidaCount += 1 + (nGapWidth / nKashidaWidth);
1369     }
1370 
1371     if( !nKashidaCount )
1372         return;
1373 
1374     // reallocate glyph array for additional kashidas
1375     // TODO: reuse array if additional glyphs would fit
1376     mnGlyphCapacity = mnGlyphCount + nKashidaCount;
1377     GlyphItem* pNewGlyphItems = new GlyphItem[ mnGlyphCapacity ];
1378     GlyphItem* pG2 = pNewGlyphItems;
1379     pG1 = mpGlyphItems;
1380     for( i = mnGlyphCount; --i >= 0; ++pG1, ++pG2 )
1381     {
1382         // default action is to copy array element
1383         *pG2 = *pG1;
1384 
1385         // only inject kashida in RTL contexts
1386         if( !pG1->IsRTLGlyph() )
1387             continue;
1388         // no kashida-injection for blank justified expansion either
1389         if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
1390             continue;
1391 
1392         // calculate gap, skip if too small
1393         int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
1394         if( 3*nGapWidth < nKashidaWidth )
1395             continue;
1396 
1397         // fill gap with kashidas
1398         nKashidaCount = 0;
1399         Point aPos = pG1->maLinearPos;
1400         aPos.X() -= nGapWidth; // cluster is already right aligned
1401         for(; nGapWidth > 0; nGapWidth -= nKashidaWidth, ++nKashidaCount )
1402         {
1403             *(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos,
1404                 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth );
1405             aPos.X() += nKashidaWidth;
1406         }
1407 
1408         // fixup rightmost kashida for gap remainder
1409         if( nGapWidth < 0 )
1410         {
1411             aPos.X() += nGapWidth;
1412             if( nKashidaCount <= 1 )
1413                 nGapWidth /= 2;               // for small gap move kashida to middle
1414             pG2[-1].mnNewWidth += nGapWidth;  // adjust kashida width to gap width
1415             pG2[-1].maLinearPos.X() += nGapWidth;
1416         }
1417 
1418         // when kashidas were inserted move the original cluster
1419         // to the right and shrink it to it's original width
1420         *pG2 = *pG1;
1421         pG2->maLinearPos.X() = aPos.X();
1422         pG2->mnNewWidth = pG2->mnOrigWidth;
1423      }
1424 
1425     // use the new glyph array
1426     DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" );
1427     delete[] mpGlyphItems;
1428     mpGlyphItems = pNewGlyphItems;
1429     mnGlyphCount = pG2 - pNewGlyphItems;
1430 }
1431 
1432 // -----------------------------------------------------------------------
1433 
1434 void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
1435 {
1436     // initialize result array
1437     long nXPos = -1;
1438     int i;
1439     for( i = 0; i < nMaxIndex; ++i )
1440         pCaretXArray[ i ] = nXPos;
1441 
1442     // calculate caret positions using glyph array
1443     const GlyphItem* pG = mpGlyphItems;
1444     for( i = mnGlyphCount; --i >= 0; ++pG )
1445     {
1446         nXPos = pG->maLinearPos.X();
1447         long nXRight = nXPos + pG->mnOrigWidth;
1448         int n = pG->mnCharPos;
1449         int nCurrIdx = 2 * (n - mnMinCharPos);
1450         if( !pG->IsRTLGlyph() )
1451         {
1452             // normal positions for LTR case
1453             pCaretXArray[ nCurrIdx ]   = nXPos;
1454             pCaretXArray[ nCurrIdx+1 ] = nXRight;
1455         }
1456         else
1457         {
1458             // reverse positions for RTL case
1459             pCaretXArray[ nCurrIdx ]   = nXRight;
1460             pCaretXArray[ nCurrIdx+1 ] = nXPos;
1461         }
1462     }
1463 }
1464 
1465 // -----------------------------------------------------------------------
1466 
1467 int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
1468 {
1469     int nCharCapacity = mnEndCharPos - mnMinCharPos;
1470     sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
1471     if( !GetCharWidths( pCharWidths ) )
1472         return STRING_LEN;
1473 
1474     long nWidth = 0;
1475     for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1476     {
1477         nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
1478         if( nWidth >= nMaxWidth )
1479             return i;
1480         nWidth += nCharExtra;
1481     }
1482 
1483     return STRING_LEN;
1484 }
1485 
1486 // -----------------------------------------------------------------------
1487 
1488 int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1489     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
1490 {
1491     const GlyphItem* pG = mpGlyphItems + nStart;
1492 
1493     // find next glyph in substring
1494     for(; nStart < mnGlyphCount; ++nStart, ++pG )
1495     {
1496         int n = pG->mnCharPos;
1497         if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
1498             break;
1499     }
1500 
1501     // return zero if no more glyph found
1502     if( nStart >= mnGlyphCount )
1503         return 0;
1504 
1505     // calculate absolute position in pixel units
1506     Point aRelativePos = pG->maLinearPos - maBasePoint;
1507 
1508     // find more glyphs which can be merged into one drawing instruction
1509     int nCount = 0;
1510     long nYPos = pG->maLinearPos.Y();
1511     long nOldFlags = pG->mnGlyphIndex;
1512     for(;;)
1513     {
1514         // update return data with glyph info
1515         ++nCount;
1516         *(pGlyphs++) = pG->mnGlyphIndex;
1517         if( pCharPosAry )
1518             *(pCharPosAry++) = pG->mnCharPos;
1519         if( pGlyphAdvAry )
1520             *pGlyphAdvAry = pG->mnNewWidth;
1521 
1522         // break at end of glyph list
1523         if( ++nStart >= mnGlyphCount )
1524             break;
1525         // break when enough glyphs
1526         if( nCount >= nLen )
1527             break;
1528 
1529         long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X();
1530         if( pGlyphAdvAry )
1531         {
1532             // override default advance width with correct value
1533             *(pGlyphAdvAry++) = nGlyphAdvance;
1534         }
1535         else
1536         {
1537             // stop when next x-position is unexpected
1538             if( pG->mnOrigWidth != nGlyphAdvance )
1539                 break;
1540         }
1541 
1542         // advance to next glyph
1543         ++pG;
1544 
1545         // stop when next y-position is unexpected
1546         if( nYPos != pG->maLinearPos.Y() )
1547             break;
1548 
1549         // stop when no longer in string
1550         int n = pG->mnCharPos;
1551         if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
1552             break;
1553 
1554         // stop when glyph flags change
1555         if( (nOldFlags ^ pG->mnGlyphIndex) & GF_FLAGMASK )
1556             break;
1557 
1558         nOldFlags = pG->mnGlyphIndex; // &GF_FLAGMASK not needed for test above
1559     }
1560 
1561     aRelativePos.X() /= mnUnitsPerPixel;
1562     aRelativePos.Y() /= mnUnitsPerPixel;
1563     rPos = GetDrawPosition( aRelativePos );
1564 
1565     return nCount;
1566 }
1567 
1568 // -----------------------------------------------------------------------
1569 
1570 void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
1571 {
1572     if( nStart >= mnGlyphCount )
1573         return;
1574 
1575     GlyphItem* pG = mpGlyphItems + nStart;
1576     // the nNewXPos argument determines the new cell position
1577     // as RTL-glyphs are right justified in their cell
1578     // the cell position needs to be adjusted to the glyph position
1579     if( pG->IsRTLGlyph() )
1580         nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
1581     // calculate the x-offset to the old position
1582     long nXDelta = nNewXPos - pG->maLinearPos.X();
1583     // adjust all following glyph positions if needed
1584     if( nXDelta != 0 )
1585     {
1586         GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
1587         for(; pG < pGEnd; ++pG )
1588             pG->maLinearPos.X() += nXDelta;
1589     }
1590 }
1591 
1592 // -----------------------------------------------------------------------
1593 
1594 void GenericSalLayout::DropGlyph( int nStart )
1595 {
1596     if( nStart >= mnGlyphCount )
1597         return;
1598     GlyphItem* pG = mpGlyphItems + nStart;
1599     pG->mnGlyphIndex = GF_DROPPED;
1600     pG->mnCharPos = -1;
1601 }
1602 
1603 // -----------------------------------------------------------------------
1604 
1605 void GenericSalLayout::Simplify( bool bIsBase )
1606 {
1607     const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
1608 
1609     // remove dropped glyphs inplace
1610     GlyphItem* pGDst = mpGlyphItems;
1611     const GlyphItem* pGSrc = mpGlyphItems;
1612     const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
1613     for(; pGSrc < pGEnd; ++pGSrc )
1614     {
1615         if( pGSrc->mnGlyphIndex == nDropMarker )
1616             continue;
1617         if( pGDst != pGSrc )
1618             *pGDst = *pGSrc;
1619         ++pGDst;
1620     }
1621     mnGlyphCount = pGDst - mpGlyphItems;
1622 }
1623 
1624 // -----------------------------------------------------------------------
1625 
1626 // make sure GlyphItems are sorted left to right
1627 void GenericSalLayout::SortGlyphItems()
1628 {
1629     // move cluster components behind their cluster start (especially for RTL)
1630     // using insertion sort because the glyph items are "almost sorted"
1631     const GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
1632     for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
1633     {
1634         // find a cluster starting with a diacritic
1635 		if( !pG->IsDiacritic() )
1636 			continue;
1637 		if( !pG->IsClusterStart() )
1638 			continue;
1639         for( GlyphItem* pBaseGlyph = pG; ++pBaseGlyph < pGEnd; )
1640         {
1641 	        // find the base glyph matching to the misplaced diacritic
1642            	if( pBaseGlyph->IsClusterStart() )
1643            		break;
1644            	if( pBaseGlyph->IsDiacritic() )
1645            		continue;
1646 
1647             // found the matching base glyph
1648             // => this base glyph becomes the new cluster start
1649             const GlyphItem aDiacritic = *pG;
1650             *pG = *pBaseGlyph;
1651             *pBaseGlyph = aDiacritic;
1652 
1653 			// update glyph flags of swapped glyphitems
1654             pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
1655             pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
1656 			// prepare for checking next cluster
1657 			pG = pBaseGlyph;
1658             break;
1659         }
1660     }
1661 }
1662 
1663 // =======================================================================
1664 
1665 MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const ImplFontData* pBaseFont )
1666 :   SalLayout()
1667 ,   mnLevel( 1 )
1668 ,   mbInComplete( false )
1669 {
1670     //maFallbackRuns[0].Clear();
1671     mpFallbackFonts[ 0 ] = pBaseFont;
1672     mpLayouts[ 0 ]  = &rBaseLayout;
1673     mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
1674 }
1675 
1676 void MultiSalLayout::SetInComplete(bool bInComplete)
1677 {
1678     mbInComplete = bInComplete;
1679     maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
1680 }
1681 
1682 // -----------------------------------------------------------------------
1683 
1684 MultiSalLayout::~MultiSalLayout()
1685 {
1686     for( int i = 0; i < mnLevel; ++i )
1687         mpLayouts[ i ]->Release();
1688 }
1689 
1690 // -----------------------------------------------------------------------
1691 
1692 bool MultiSalLayout::AddFallback( SalLayout& rFallback,
1693     ImplLayoutRuns& rFallbackRuns, const ImplFontData* pFallbackFont )
1694 {
1695     if( mnLevel >= MAX_FALLBACK )
1696         return false;
1697 
1698     mpFallbackFonts[ mnLevel ]  = pFallbackFont;
1699     mpLayouts[ mnLevel ]        = &rFallback;
1700     maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1701     ++mnLevel;
1702     return true;
1703 }
1704 
1705 // -----------------------------------------------------------------------
1706 
1707 bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
1708 {
1709     if( mnLevel <= 1 )
1710         return false;
1711     if (!mbInComplete)
1712         maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1713     return true;
1714 }
1715 
1716 // -----------------------------------------------------------------------
1717 
1718 void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1719 {
1720     SalLayout::AdjustLayout( rArgs );
1721     ImplLayoutArgs aMultiArgs = rArgs;
1722 
1723     if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1724     {
1725         // for stretched text in a MultiSalLayout the target width needs to be
1726         // distributed by individually adjusting its virtual character widths
1727         long nTargetWidth = aMultiArgs.mnLayoutWidth;
1728         nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1729         aMultiArgs.mnLayoutWidth = 0;
1730 
1731         // we need to get the original unmodified layouts ready
1732         for( int n = 0; n < mnLevel; ++n )
1733             mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1734         // then we can measure the unmodified metrics
1735         int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1736         sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
1737         FillDXArray( pJustificationArray );
1738         // #i17359# multilayout is not simplified yet, so calculating the
1739         // unjustified width needs handholding; also count the number of
1740         // stretchable virtual char widths
1741         long nOrigWidth = 0;
1742         int nStretchable = 0;
1743         for( int i = 0; i < nCharCount; ++i )
1744         {
1745             // convert array from widths to sum of widths
1746             nOrigWidth += pJustificationArray[i];
1747             if( pJustificationArray[i] > 0 )
1748                 ++nStretchable;
1749         }
1750 
1751         // now we are able to distribute the extra width over the virtual char widths
1752         if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1753         {
1754             int nDiffWidth = nTargetWidth - nOrigWidth;
1755             int nWidthSum = 0;
1756             for( int i = 0; i < nCharCount; ++i )
1757             {
1758                 int nJustWidth = pJustificationArray[i];
1759                 if( (nJustWidth > 0) && (nStretchable > 0) )
1760                 {
1761                     int nDeltaWidth = nDiffWidth / nStretchable;
1762                     nJustWidth += nDeltaWidth;
1763                     nDiffWidth -= nDeltaWidth;
1764                     --nStretchable;
1765                 }
1766                 nWidthSum += nJustWidth;
1767                 pJustificationArray[i] = nWidthSum;
1768             }
1769             if( nWidthSum != nTargetWidth )
1770                 pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1771 
1772             // the justification array is still in base level units
1773             // => convert it to pixel units
1774             if( mnUnitsPerPixel > 1 )
1775             {
1776                 for( int i = 0; i < nCharCount; ++i )
1777                 {
1778                     sal_Int32 nVal = pJustificationArray[ i ];
1779                     nVal += (mnUnitsPerPixel + 1) / 2;
1780                     pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1781                 }
1782             }
1783 
1784             // change the mpDXArray temporarilly (just for the justification)
1785             aMultiArgs.mpDXArray = pJustificationArray;
1786         }
1787     }
1788 
1789     // Compute rtl flags, since in some scripts glyphs/char order can be
1790     // reversed for a few character sequencies e.g. Myanmar
1791     std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1792     rArgs.ResetPos();
1793     bool bRtl;
1794     int nRunStart, nRunEnd;
1795     while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1796     {
1797         if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1798                             vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1799     }
1800     rArgs.ResetPos();
1801 
1802     // prepare "merge sort"
1803     int nStartOld[ MAX_FALLBACK ];
1804     int nStartNew[ MAX_FALLBACK ];
1805     int nCharPos[ MAX_FALLBACK ];
1806     sal_Int32 nGlyphAdv[ MAX_FALLBACK ];
1807     int nValid[ MAX_FALLBACK ] = {0};
1808 
1809     sal_GlyphId nDummy;
1810     Point aPos;
1811     int nLevel = 0, n;
1812     for( n = 0; n < mnLevel; ++n )
1813     {
1814         // now adjust the individual components
1815         if( n > 0 )
1816         {
1817             aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1818             aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
1819         }
1820         mpLayouts[n]->AdjustLayout( aMultiArgs );
1821 
1822         // disable glyph-injection for glyph-fallback SalLayout iteration
1823         mpLayouts[n]->DisableGlyphInjection( true );
1824 
1825         // remove unused parts of component
1826         if( n > 0 )
1827         {
1828             if (mbInComplete && (n == mnLevel-1))
1829                 mpLayouts[n]->Simplify( true );
1830             else
1831                 mpLayouts[n]->Simplify( false );
1832         }
1833 
1834         // prepare merging components
1835         nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1836         nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1837             nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] );
1838 #ifdef MULTI_SL_DEBUG
1839         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],
1840             rArgs.mpStr[nCharPos[nLevel]]);
1841 #endif
1842         if( (n > 0) && !nValid[ nLevel ] )
1843         {
1844             // an empty fallback layout can be released
1845             mpLayouts[n]->Release();
1846         }
1847         else
1848         {
1849             // reshuffle used fallbacks if needed
1850             if( nLevel != n )
1851             {
1852                 mpLayouts[ nLevel ]         = mpLayouts[ n ];
1853                 mpFallbackFonts[ nLevel ]   = mpFallbackFonts[ n ];
1854                 maFallbackRuns[ nLevel ]    = maFallbackRuns[ n ];
1855             }
1856             ++nLevel;
1857         }
1858     }
1859     mnLevel = nLevel;
1860 
1861     // merge the fallback levels
1862     long nXPos = 0;
1863     double fUnitMul = 1.0;
1864     for( n = 0; n < nLevel; ++n )
1865         maFallbackRuns[n].ResetPos();
1866     int nActiveCharPos = nCharPos[0];
1867     int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
1868         rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1869     int nRunVisibleEndChar = nCharPos[0];
1870     while( nValid[0] && (nLevel > 0))
1871     {
1872         // find best fallback level
1873         for( n = 0; n < nLevel; ++n )
1874             if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1875                 // fallback level n wins when it requested no further fallback
1876                 break;
1877         int nFBLevel = n;
1878 
1879         if( n < nLevel )
1880         {
1881             // use base(n==0) or fallback(n>=1) level
1882             fUnitMul = mnUnitsPerPixel;
1883             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1884             long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1885             mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1886         }
1887         else
1888         {
1889             n = 0;  // keep NotDef in base level
1890             fUnitMul = 1.0;
1891         }
1892 
1893         if( n > 0 )
1894         {
1895             // drop the NotDef glyphs in the base layout run if a fallback run exists
1896             while (
1897                     (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
1898                     (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
1899                   )
1900             {
1901                 mpLayouts[0]->DropGlyph( nStartOld[0] );
1902                 nStartOld[0] = nStartNew[0];
1903                 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
1904                     nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
1905 #ifdef MULTI_SL_DEBUG
1906                 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]]);
1907 #endif
1908                 if( !nValid[0] )
1909                    break;
1910             }
1911         }
1912 
1913         // skip to end of layout run and calculate its advance width
1914         int nRunAdvance = 0;
1915         bool bKeepNotDef = (nFBLevel >= nLevel);
1916         for(;;)
1917         {
1918             nRunAdvance += nGlyphAdv[n];
1919 
1920             // proceed to next glyph
1921             nStartOld[n] = nStartNew[n];
1922             int nOrigCharPos = nCharPos[n];
1923             nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1924                 nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
1925 #ifdef MULTI_SL_DEBUG
1926             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]]);
1927 #endif
1928             // break after last glyph of active layout
1929             if( !nValid[n] )
1930             {
1931                 // performance optimization (when a fallback layout is no longer needed)
1932                 if( n >= nLevel-1 )
1933                     --nLevel;
1934                 break;
1935             }
1936 
1937             //If the next character is one which belongs to the next level, then we
1938             //are finished here for now, and we'll pick up after the next level has
1939             //been processed
1940             if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
1941             {
1942                 if (nOrigCharPos < nCharPos[n])
1943                 {
1944                     if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
1945                         break;
1946                 }
1947                 else if (nOrigCharPos > nCharPos[n])
1948                 {
1949                     if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
1950                         break;
1951                 }
1952             }
1953 
1954             // break at end of layout run
1955             if( n > 0 )
1956             {
1957                 // skip until end of fallback run
1958                 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
1959                     break;
1960             }
1961             else
1962             {
1963                 // break when a fallback is needed and available
1964                 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
1965                 if( bNeedFallback )
1966                     if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
1967                         break;
1968                 // break when change from resolved to unresolved base layout run
1969                 if( bKeepNotDef && !bNeedFallback )
1970                     { maFallbackRuns[0].NextRun(); break; }
1971                 bKeepNotDef = bNeedFallback;
1972             }
1973             // check for reordered glyphs
1974             if (aMultiArgs.mpDXArray &&
1975                 nRunVisibleEndChar < mnEndCharPos &&
1976                 nRunVisibleEndChar >= mnMinCharPos &&
1977                 nCharPos[n] < mnEndCharPos &&
1978                 nCharPos[n] >= mnMinCharPos)
1979             {
1980                 if (vRtl[nActiveCharPos - mnMinCharPos])
1981                 {
1982                     if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1983                         >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1984                     {
1985                         nRunVisibleEndChar = nCharPos[n];
1986                     }
1987                 }
1988                 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1989                          <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1990                 {
1991                     nRunVisibleEndChar = nCharPos[n];
1992                 }
1993             }
1994         }
1995 
1996         // if a justification array is available
1997         // => use it directly to calculate the corresponding run width
1998         if( aMultiArgs.mpDXArray )
1999         {
2000             // the run advance is the width from the first char
2001             // in the run to the first char in the next run
2002             nRunAdvance = 0;
2003 #ifdef MULTI_SL_DEBUG
2004             const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
2005             int nOldRunAdv = 0;
2006             int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
2007             if( nDXIndex >= 0 )
2008                 nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
2009             nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
2010             if( nDXIndex >= 0 )
2011                 nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
2012             if( !bLTR )
2013                 nOldRunAdv = -nOldRunAdv;
2014 #endif
2015             if (vRtl[nActiveCharPos - mnMinCharPos])
2016             {
2017               if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
2018                   nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
2019               if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
2020                   nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
2021 #ifdef MULTI_SL_DEBUG
2022               fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
2023 #endif
2024             }
2025             else
2026             {
2027                 if (nRunVisibleEndChar >= mnMinCharPos)
2028                   nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
2029                 if (nLastRunEndChar >= mnMinCharPos)
2030                   nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
2031 #ifdef MULTI_SL_DEBUG
2032                 fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
2033 #endif
2034             }
2035             nLastRunEndChar = nRunVisibleEndChar;
2036             nRunVisibleEndChar = nCharPos[0];
2037             // the requested width is still in pixel units
2038             // => convert it to base level font units
2039             nRunAdvance *= mnUnitsPerPixel;
2040         }
2041         else
2042         {
2043             // the measured width is still in fallback font units
2044             // => convert it to base level font units
2045             if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
2046                 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
2047         }
2048 
2049         // calculate new x position (in base level units)
2050         nXPos += nRunAdvance;
2051 
2052         // prepare for next fallback run
2053         nActiveCharPos = nCharPos[0];
2054         // it essential that the runs don't get ahead of themselves and in the
2055         // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
2056         // have already been reached on the base level
2057         for( int i = nFBLevel; --i >= 0;)
2058         {
2059             if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
2060             {
2061                 if (bRtl)
2062                 {
2063                     if (nRunStart > nActiveCharPos)
2064                         maFallbackRuns[i].NextRun();
2065                 }
2066                 else
2067                 {
2068                     if (nRunEnd <= nActiveCharPos)
2069                         maFallbackRuns[i].NextRun();
2070                 }
2071             }
2072         }
2073 //            if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) )
2074 //                maFallbackRuns[i].NextRun();
2075     }
2076 
2077     mpLayouts[0]->Simplify( true );
2078 
2079     // reenable glyph-injection
2080     for( n = 0; n < mnLevel; ++n )
2081         mpLayouts[n]->DisableGlyphInjection( false );
2082 }
2083 
2084 // -----------------------------------------------------------------------
2085 
2086 void MultiSalLayout::InitFont() const
2087 {
2088     if( mnLevel > 0 )
2089         mpLayouts[0]->InitFont();
2090 }
2091 
2092 // -----------------------------------------------------------------------
2093 
2094 const ImplFontData* MultiSalLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const
2095 {
2096     int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
2097     return mpFallbackFonts[ nFallbackLevel ];
2098 }
2099 
2100 // -----------------------------------------------------------------------
2101 
2102 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
2103 {
2104     for( int i = mnLevel; --i >= 0; )
2105     {
2106         SalLayout& rLayout = *mpLayouts[ i ];
2107         rLayout.DrawBase() += maDrawBase;
2108         rLayout.DrawOffset() += maDrawOffset;
2109         rLayout.InitFont();
2110         rLayout.DrawText( rGraphics );
2111         rLayout.DrawOffset() -= maDrawOffset;
2112         rLayout.DrawBase() -= maDrawBase;
2113     }
2114     // NOTE: now the baselevel font is active again
2115 }
2116 
2117  // -----------------------------------------------------------------------
2118 
2119 int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2120 {
2121     if( mnLevel <= 0 )
2122         return STRING_LEN;
2123     if( mnLevel == 1 )
2124         return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
2125 
2126     int nCharCount = mnEndCharPos - mnMinCharPos;
2127     sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
2128     mpLayouts[0]->FillDXArray( pCharWidths );
2129 
2130     for( int n = 1; n < mnLevel; ++n )
2131     {
2132         SalLayout& rLayout = *mpLayouts[ n ];
2133         rLayout.FillDXArray( pCharWidths + nCharCount );
2134         double fUnitMul = mnUnitsPerPixel;
2135         fUnitMul /= rLayout.GetUnitsPerPixel();
2136         for( int i = 0; i < nCharCount; ++i )
2137         {
2138             long w = pCharWidths[ i + nCharCount ];
2139             w = static_cast<long>(w*fUnitMul + 0.5);
2140             pCharWidths[ i ] += w;
2141         }
2142     }
2143 
2144     long nWidth = 0;
2145     for( int i = 0; i < nCharCount; ++i )
2146     {
2147         nWidth += pCharWidths[ i ] * nFactor;
2148         if( nWidth > nMaxWidth )
2149             return (i + mnMinCharPos);
2150         nWidth += nCharExtra;
2151     }
2152 
2153     return STRING_LEN;
2154 }
2155 
2156 // -----------------------------------------------------------------------
2157 
2158 long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
2159 {
2160     long nMaxWidth = 0;
2161 
2162     // prepare merging of fallback levels
2163     sal_Int32* pTempWidths = NULL;
2164     const int nCharCount = mnEndCharPos - mnMinCharPos;
2165     if( pCharWidths )
2166     {
2167         for( int i = 0; i < nCharCount; ++i )
2168             pCharWidths[i] = 0;
2169         pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
2170     }
2171 
2172     for( int n = mnLevel; --n >= 0; )
2173     {
2174         // query every fallback level
2175         long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
2176         if( !nTextWidth )
2177             continue;
2178         // merge results from current level
2179         double fUnitMul = mnUnitsPerPixel;
2180         fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2181         nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
2182         if( nMaxWidth < nTextWidth )
2183             nMaxWidth = nTextWidth;
2184         if( !pCharWidths )
2185             continue;
2186         // calculate virtual char widths using most probable fallback layout
2187         for( int i = 0; i < nCharCount; ++i )
2188         {
2189             // #i17359# restriction:
2190             // one char cannot be resolved from different fallbacks
2191             if( pCharWidths[i] != 0 )
2192                 continue;
2193             long nCharWidth = pTempWidths[i];
2194             if( !nCharWidth )
2195                 continue;
2196             nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
2197             pCharWidths[i] = nCharWidth;
2198         }
2199     }
2200 
2201     return nMaxWidth;
2202 }
2203 
2204 // -----------------------------------------------------------------------
2205 
2206 void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
2207 {
2208     SalLayout& rLayout = *mpLayouts[ 0 ];
2209     rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
2210 
2211     if( mnLevel > 1 )
2212     {
2213         sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
2214         for( int n = 1; n < mnLevel; ++n )
2215         {
2216             mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
2217             double fUnitMul = mnUnitsPerPixel;
2218             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2219             for( int i = 0; i < nMaxIndex; ++i )
2220                 if( pTempPos[i] >= 0 )
2221                 {
2222                     long w = pTempPos[i];
2223                     w = static_cast<long>(w*fUnitMul + 0.5);
2224                     pCaretXArray[i] = w;
2225                 }
2226         }
2227     }
2228 }
2229 
2230 // -----------------------------------------------------------------------
2231 
2232 int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
2233     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
2234 {
2235     // for multi-level fallback only single glyphs should be used
2236     if( mnLevel > 1 && nLen > 1 )
2237         nLen = 1;
2238 
2239     // NOTE: nStart is tagged with current font index
2240     int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
2241     nStart &= ~GF_FONTMASK;
2242     for(; nLevel < mnLevel; ++nLevel, nStart=0 )
2243     {
2244         SalLayout& rLayout = *mpLayouts[ nLevel ];
2245         rLayout.InitFont();
2246         int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
2247             nStart, pGlyphAdvAry, pCharPosAry );
2248         if( nRetVal )
2249         {
2250             int nFontTag = nLevel << GF_FONTSHIFT;
2251             nStart |= nFontTag;
2252             double fUnitMul = mnUnitsPerPixel;
2253             fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
2254             for( int i = 0; i < nRetVal; ++i )
2255             {
2256                 if( pGlyphAdvAry )
2257                 {
2258                     long w = pGlyphAdvAry[i];
2259                     w = static_cast<long>(w * fUnitMul + 0.5);
2260                     pGlyphAdvAry[i] = w;
2261                 }
2262                 pGlyphIdxAry[ i ] |= nFontTag;
2263             }
2264             rPos += maDrawBase;
2265             rPos += maDrawOffset;
2266             return nRetVal;
2267         }
2268     }
2269 
2270     // #111016# reset to base level font when done
2271     mpLayouts[0]->InitFont();
2272     return 0;
2273 }
2274 
2275 // -----------------------------------------------------------------------
2276 
2277 bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
2278     ::basegfx::B2DPolyPolygonVector& rPPV ) const
2279 {
2280     bool bRet = false;
2281 
2282     for( int i = mnLevel; --i >= 0; )
2283     {
2284         SalLayout& rLayout = *mpLayouts[ i ];
2285         rLayout.DrawBase() = maDrawBase;
2286         rLayout.DrawOffset() += maDrawOffset;
2287         rLayout.InitFont();
2288         bRet |= rLayout.GetOutline( rGraphics, rPPV );
2289         rLayout.DrawOffset() -= maDrawOffset;
2290     }
2291 
2292     return bRet;
2293 }
2294 
2295 // -----------------------------------------------------------------------
2296 
2297 bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
2298 {
2299     bool bRet = false;
2300 
2301     Rectangle aRectangle;
2302     for( int i = mnLevel; --i >= 0; )
2303     {
2304         SalLayout& rLayout = *mpLayouts[ i ];
2305         rLayout.DrawBase() = maDrawBase;
2306         rLayout.DrawOffset() += maDrawOffset;
2307         rLayout.InitFont();
2308         if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
2309         {
2310             rRect.Union( aRectangle );
2311             bRet = true;
2312         }
2313         rLayout.DrawOffset() -= maDrawOffset;
2314     }
2315 
2316     return bRet;
2317 }
2318 
2319 // =======================================================================
2320