xref: /aoo4110/main/vcl/source/gdi/sallayout.cxx (revision b1cdbd2c)
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;
mslLog()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
IsDiacritic(sal_UCS4 nChar)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 
GetVerticalFlags(sal_UCS4 nChar)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 
GetVerticalChar(sal_UCS4)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 
GetMirroredChar(sal_UCS4 nChar)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
GetAutofallback(sal_UCS4 nChar)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 
GetLocalizedChar(sal_UCS4 nChar,LanguageType eLang)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 
IsControlChar(sal_UCS4 cChar)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 
AddPos(int nCharPos,bool bRTL)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 
AddRun(int nCharPos0,int nCharPos1,bool bRTL)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 
PosIsInRun(int nCharPos) const467 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 
PosIsInAnyRun(int nCharPos) const488 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 
GetNextPos(int * nCharPos,bool * bRightToLeft)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 
GetRun(int * nMinRunPos,int * nEndRunPos,bool * bRightToLeft) const557 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 
ImplLayoutArgs(const xub_Unicode * pStr,int nLen,int nMinCharPos,int nEndCharPos,int nFlags)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
AddRun(int nCharPos0,int nCharPos1,bool bRTL)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 
PrepareFallback()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 
GetNextRun(int * nMinRunPos,int * nEndRunPos,bool * bRTL)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 
SalLayout()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 
~SalLayout()755 SalLayout::~SalLayout()
756 {}
757 
758 // -----------------------------------------------------------------------
759 
AdjustLayout(ImplLayoutArgs & rArgs)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 
Reference() const770 void SalLayout::Reference() const
771 {
772     // TODO: protect when multiple threads can access this
773     ++mnRefCount;
774 }
775 
776 // -----------------------------------------------------------------------
777 
Release() const778 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 
GetDrawPosition(const Point & rRelative) const789 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 
CalcAsianKerning(sal_UCS4 c,bool bLeft,bool)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 
GetOutline(SalGraphics & rSalGraphics,::basegfx::B2DPolyPolygonVector & rVector) const870 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 
GetBoundRect(SalGraphics & rSalGraphics,Rectangle & rRect) const906 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 
IsSpacingGlyph(sal_GlyphId nGlyph) const934 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 
GetFallbackFontData(sal_GlyphId) const952 const ImplFontData* SalLayout::GetFallbackFontData( sal_GlyphId /*aGlyphId*/ ) const
953 {
954 #if 0
955     int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT
956     assert( nFallbackLevel == 0 );
957 #endif
958     return NULL;
959 }
960 
961 // =======================================================================
962 
GenericSalLayout()963 GenericSalLayout::GenericSalLayout()
964 :   mpGlyphItems(0),
965     mnGlyphCount(0),
966     mnGlyphCapacity(0)
967 {}
968 
969 // -----------------------------------------------------------------------
970 
~GenericSalLayout()971 GenericSalLayout::~GenericSalLayout()
972 {
973     delete[] mpGlyphItems;
974 }
975 
976 // -----------------------------------------------------------------------
977 
AppendGlyph(const GlyphItem & rGlyphItem)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 
GetCharWidths(sal_Int32 * pCharWidths) const999 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 
FillDXArray(sal_Int32 * pCharWidths) const1076 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
GetTextWidth() const1089 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 
AdjustLayout(ImplLayoutArgs & rArgs)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 
ApplyDXArray(ImplLayoutArgs & rArgs)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 
Justify(long nNewWidth)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 
ApplyAsianKerning(const sal_Unicode * pStr,int nLength)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 
KashidaJustify(long nKashidaIndex,int nKashidaWidth)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->maGlyphId ) )
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->maGlyphId ) )
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 
GetCaretPositions(int nMaxIndex,sal_Int32 * pCaretXArray) const1439 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 
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const1472 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 
GetNextGlyphs(int nLen,sal_GlyphId * pGlyphs,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvAry,int * pCharPosAry) const1493 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->maGlyphId;
1517     for(;;)
1518     {
1519         // update return data with glyph info
1520         ++nCount;
1521         *(pGlyphs++) = pG->maGlyphId;
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->maGlyphId) & GF_FLAGMASK )
1561             break;
1562 
1563         nOldFlags = pG->maGlyphId; // &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 
MoveGlyph(int nStart,long nNewXPos)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 
DropGlyph(int nStart)1599 void GenericSalLayout::DropGlyph( int nStart )
1600 {
1601     if( nStart >= mnGlyphCount )
1602         return;
1603     GlyphItem* pG = mpGlyphItems + nStart;
1604     pG->maGlyphId = GF_DROPPED;
1605     pG->mnCharPos = -1;
1606 }
1607 
1608 // -----------------------------------------------------------------------
1609 
Simplify(bool bIsBase)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->maGlyphId == 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
SortGlyphItems()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 
MultiSalLayout(SalLayout & rBaseLayout,const ImplFontData * pBaseFont)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 
SetInComplete(bool bInComplete)1681 void MultiSalLayout::SetInComplete(bool bInComplete)
1682 {
1683     mbInComplete = bInComplete;
1684     maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
1685 }
1686 
1687 // -----------------------------------------------------------------------
1688 
~MultiSalLayout()1689 MultiSalLayout::~MultiSalLayout()
1690 {
1691     for( int i = 0; i < mnLevel; ++i )
1692         mpLayouts[ i ]->Release();
1693 }
1694 
1695 // -----------------------------------------------------------------------
1696 
AddFallback(SalLayout & rFallback,ImplLayoutRuns & rFallbackRuns,const ImplFontData * pFallbackFont)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 
LayoutText(ImplLayoutArgs & rArgs)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 
AdjustLayout(ImplLayoutArgs & rArgs)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     int nActiveCharPos = nCharPos[0];
1873     // get the end index of the active run
1874     // nActiveCharPos can be less than mnMinCharPos if the ScriptItemize() call
1875     // in UniscribeLayout::LayoutText() finds a mult-character script item
1876     // that starts in the leading context and overlaps into the start of the
1877     // run.  UniscribeLayout::LayoutText() drops the glyphs for the context
1878     // characters, but does not (yet?) adjust the starting character position
1879     // of the item.   Since the text layout direction can't change in the
1880     // middle of the offending script item, we can safely use the direction at
1881     // the start of the run
1882     int nLastRunEndChar = (vRtl[nActiveCharPos >= mnMinCharPos ? (nActiveCharPos - mnMinCharPos) : 0])?
1883         rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1884     int nRunVisibleEndChar = nCharPos[0];
1885     // merge the fallback levels
1886     while( nValid[0] && (nLevel > 0))
1887     {
1888         // find best fallback level
1889         for( n = 0; n < nLevel; ++n )
1890             if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1891                 // fallback level n wins when it requested no further fallback
1892                 break;
1893         int nFBLevel = n;
1894 
1895         if( n < nLevel )
1896         {
1897             // use base(n==0) or fallback(n>=1) level
1898             fUnitMul = mnUnitsPerPixel;
1899             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1900             long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1901             mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1902         }
1903         else
1904         {
1905             n = 0;  // keep NotDef in base level
1906             fUnitMul = 1.0;
1907         }
1908 
1909         if( n > 0 )
1910         {
1911             // drop the NotDef glyphs in the base layout run if a fallback run exists
1912             while (
1913                     (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
1914                     (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
1915                   )
1916             {
1917                 mpLayouts[0]->DropGlyph( nStartOld[0] );
1918                 nStartOld[0] = nStartNew[0];
1919                 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
1920                     nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
1921 #ifdef MULTI_SL_DEBUG
1922                 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]]);
1923 #endif
1924                 if( !nValid[0] )
1925                    break;
1926             }
1927         }
1928 
1929         // skip to end of layout run and calculate its advance width
1930         int nRunAdvance = 0;
1931         bool bKeepNotDef = (nFBLevel >= nLevel);
1932         for(;;)
1933         {
1934             nRunAdvance += nGlyphAdv[n];
1935 
1936             // proceed to next glyph
1937             nStartOld[n] = nStartNew[n];
1938             int nOrigCharPos = nCharPos[n];
1939             nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1940                 nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
1941 #ifdef MULTI_SL_DEBUG
1942             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]]);
1943 #endif
1944             // break after last glyph of active layout
1945             if( !nValid[n] )
1946             {
1947                 // performance optimization (when a fallback layout is no longer needed)
1948                 if( n >= nLevel-1 )
1949                     --nLevel;
1950                 break;
1951             }
1952 
1953             //If the next character is one which belongs to the next level, then we
1954             //are finished here for now, and we'll pick up after the next level has
1955             //been processed
1956             if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
1957             {
1958                 if (nOrigCharPos < nCharPos[n])
1959                 {
1960                     if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
1961                         break;
1962                 }
1963                 else if (nOrigCharPos > nCharPos[n])
1964                 {
1965                     if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
1966                         break;
1967                 }
1968             }
1969 
1970             // break at end of layout run
1971             if( n > 0 )
1972             {
1973                 // skip until end of fallback run
1974                 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
1975                     break;
1976             }
1977             else
1978             {
1979                 // break when a fallback is needed and available
1980                 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
1981                 if( bNeedFallback )
1982                     if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
1983                         break;
1984                 // break when change from resolved to unresolved base layout run
1985                 if( bKeepNotDef && !bNeedFallback )
1986                     { maFallbackRuns[0].NextRun(); break; }
1987                 bKeepNotDef = bNeedFallback;
1988             }
1989             // check for reordered glyphs
1990             if (aMultiArgs.mpDXArray &&
1991                 nRunVisibleEndChar < mnEndCharPos &&
1992                 nRunVisibleEndChar >= mnMinCharPos &&
1993                 nCharPos[n] < mnEndCharPos &&
1994                 nCharPos[n] >= mnMinCharPos)
1995             {
1996                 if (vRtl[nActiveCharPos - mnMinCharPos])
1997                 {
1998                     if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1999                         >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
2000                     {
2001                         nRunVisibleEndChar = nCharPos[n];
2002                     }
2003                 }
2004                 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
2005                          <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
2006                 {
2007                     nRunVisibleEndChar = nCharPos[n];
2008                 }
2009             }
2010         }
2011 
2012         // if a justification array is available
2013         // => use it directly to calculate the corresponding run width
2014         if( aMultiArgs.mpDXArray )
2015         {
2016             // the run advance is the width from the first char
2017             // in the run to the first char in the next run
2018             nRunAdvance = 0;
2019 #ifdef MULTI_SL_DEBUG
2020             const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
2021             int nOldRunAdv = 0;
2022             int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
2023             if( nDXIndex >= 0 )
2024                 nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
2025             nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
2026             if( nDXIndex >= 0 )
2027                 nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
2028             if( !bLTR )
2029                 nOldRunAdv = -nOldRunAdv;
2030 #endif
2031             if (vRtl[nActiveCharPos - mnMinCharPos])
2032             {
2033               if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
2034                   nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
2035               if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
2036                   nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
2037 #ifdef MULTI_SL_DEBUG
2038               fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
2039 #endif
2040             }
2041             else
2042             {
2043                 if (nRunVisibleEndChar >= mnMinCharPos)
2044                   nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
2045                 if (nLastRunEndChar >= mnMinCharPos)
2046                   nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
2047 #ifdef MULTI_SL_DEBUG
2048                 fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
2049 #endif
2050             }
2051             nLastRunEndChar = nRunVisibleEndChar;
2052             nRunVisibleEndChar = nCharPos[0];
2053             // the requested width is still in pixel units
2054             // => convert it to base level font units
2055             nRunAdvance *= mnUnitsPerPixel;
2056         }
2057         else
2058         {
2059             // the measured width is still in fallback font units
2060             // => convert it to base level font units
2061             if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
2062                 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
2063         }
2064 
2065         // calculate new x position (in base level units)
2066         nXPos += nRunAdvance;
2067 
2068         // prepare for next fallback run
2069         nActiveCharPos = nCharPos[0];
2070         // it essential that the runs don't get ahead of themselves and in the
2071         // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
2072         // have already been reached on the base level
2073         for( int i = nFBLevel; --i >= 0;)
2074         {
2075             if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
2076             {
2077                 if (bRtl)
2078                 {
2079                     if (nRunStart > nActiveCharPos)
2080                         maFallbackRuns[i].NextRun();
2081                 }
2082                 else
2083                 {
2084                     if (nRunEnd <= nActiveCharPos)
2085                         maFallbackRuns[i].NextRun();
2086                 }
2087             }
2088         }
2089     }
2090 
2091     mpLayouts[0]->Simplify( true );
2092 
2093     // reenable glyph-injection
2094     for( n = 0; n < mnLevel; ++n )
2095         mpLayouts[n]->DisableGlyphInjection( false );
2096 }
2097 
2098 // -----------------------------------------------------------------------
2099 
InitFont() const2100 void MultiSalLayout::InitFont() const
2101 {
2102     if( mnLevel > 0 )
2103         mpLayouts[0]->InitFont();
2104 }
2105 
2106 // -----------------------------------------------------------------------
2107 
GetFallbackFontData(sal_GlyphId aGlyphId) const2108 const ImplFontData* MultiSalLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const
2109 {
2110     int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
2111     return mpFallbackFonts[ nFallbackLevel ];
2112 }
2113 
2114 // -----------------------------------------------------------------------
2115 
DrawText(SalGraphics & rGraphics) const2116 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
2117 {
2118     for( int i = mnLevel; --i >= 0; )
2119     {
2120         SalLayout& rLayout = *mpLayouts[ i ];
2121         rLayout.DrawBase() += maDrawBase;
2122         rLayout.DrawOffset() += maDrawOffset;
2123         rLayout.InitFont();
2124         rLayout.DrawText( rGraphics );
2125         rLayout.DrawOffset() -= maDrawOffset;
2126         rLayout.DrawBase() -= maDrawBase;
2127     }
2128     // NOTE: now the baselevel font is active again
2129 }
2130 
2131  // -----------------------------------------------------------------------
2132 
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const2133 int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2134 {
2135     if( mnLevel <= 0 )
2136         return STRING_LEN;
2137     if( mnLevel == 1 )
2138         return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
2139 
2140     int nCharCount = mnEndCharPos - mnMinCharPos;
2141     sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
2142     mpLayouts[0]->FillDXArray( pCharWidths );
2143 
2144     for( int n = 1; n < mnLevel; ++n )
2145     {
2146         SalLayout& rLayout = *mpLayouts[ n ];
2147         rLayout.FillDXArray( pCharWidths + nCharCount );
2148         double fUnitMul = mnUnitsPerPixel;
2149         fUnitMul /= rLayout.GetUnitsPerPixel();
2150         for( int i = 0; i < nCharCount; ++i )
2151         {
2152             long w = pCharWidths[ i + nCharCount ];
2153             w = static_cast<long>(w*fUnitMul + 0.5);
2154             pCharWidths[ i ] += w;
2155         }
2156     }
2157 
2158     long nWidth = 0;
2159     for( int i = 0; i < nCharCount; ++i )
2160     {
2161         nWidth += pCharWidths[ i ] * nFactor;
2162         if( nWidth > nMaxWidth )
2163             return (i + mnMinCharPos);
2164         nWidth += nCharExtra;
2165     }
2166 
2167     return STRING_LEN;
2168 }
2169 
2170 // -----------------------------------------------------------------------
2171 
FillDXArray(sal_Int32 * pCharWidths) const2172 long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
2173 {
2174     long nMaxWidth = 0;
2175 
2176     // prepare merging of fallback levels
2177     sal_Int32* pTempWidths = NULL;
2178     const int nCharCount = mnEndCharPos - mnMinCharPos;
2179     if( pCharWidths )
2180     {
2181         for( int i = 0; i < nCharCount; ++i )
2182             pCharWidths[i] = 0;
2183         pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
2184     }
2185 
2186     for( int n = mnLevel; --n >= 0; )
2187     {
2188         // query every fallback level
2189         long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
2190         if( !nTextWidth )
2191             continue;
2192         // merge results from current level
2193         double fUnitMul = mnUnitsPerPixel;
2194         fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2195         nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
2196         if( nMaxWidth < nTextWidth )
2197             nMaxWidth = nTextWidth;
2198         if( !pCharWidths )
2199             continue;
2200         // calculate virtual char widths using most probable fallback layout
2201         for( int i = 0; i < nCharCount; ++i )
2202         {
2203             // #i17359# restriction:
2204             // one char cannot be resolved from different fallbacks
2205             if( pCharWidths[i] != 0 )
2206                 continue;
2207             long nCharWidth = pTempWidths[i];
2208             if( !nCharWidth )
2209                 continue;
2210             nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
2211             pCharWidths[i] = nCharWidth;
2212         }
2213     }
2214 
2215     return nMaxWidth;
2216 }
2217 
2218 // -----------------------------------------------------------------------
2219 
GetCaretPositions(int nMaxIndex,sal_Int32 * pCaretXArray) const2220 void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
2221 {
2222     SalLayout& rLayout = *mpLayouts[ 0 ];
2223     rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
2224 
2225     if( mnLevel > 1 )
2226     {
2227         sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
2228         for( int n = 1; n < mnLevel; ++n )
2229         {
2230             mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
2231             double fUnitMul = mnUnitsPerPixel;
2232             fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2233             for( int i = 0; i < nMaxIndex; ++i )
2234                 if( pTempPos[i] >= 0 )
2235                 {
2236                     long w = pTempPos[i];
2237                     w = static_cast<long>(w*fUnitMul + 0.5);
2238                     pCaretXArray[i] = w;
2239                 }
2240         }
2241     }
2242 }
2243 
2244 // -----------------------------------------------------------------------
2245 
GetNextGlyphs(int nLen,sal_GlyphId * pGlyphIdxAry,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvAry,int * pCharPosAry) const2246 int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
2247     int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
2248 {
2249     // for multi-level fallback only single glyphs should be used
2250     if( mnLevel > 1 && nLen > 1 )
2251         nLen = 1;
2252 
2253     // NOTE: nStart is tagged with current font index
2254     int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
2255     nStart &= ~GF_FONTMASK;
2256     for(; nLevel < mnLevel; ++nLevel, nStart=0 )
2257     {
2258         SalLayout& rLayout = *mpLayouts[ nLevel ];
2259         rLayout.InitFont();
2260         int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
2261             nStart, pGlyphAdvAry, pCharPosAry );
2262         if( nRetVal )
2263         {
2264             int nFontTag = nLevel << GF_FONTSHIFT;
2265             nStart |= nFontTag;
2266             double fUnitMul = mnUnitsPerPixel;
2267             fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
2268             for( int i = 0; i < nRetVal; ++i )
2269             {
2270                 if( pGlyphAdvAry )
2271                 {
2272                     long w = pGlyphAdvAry[i];
2273                     w = static_cast<long>(w * fUnitMul + 0.5);
2274                     pGlyphAdvAry[i] = w;
2275                 }
2276                 pGlyphIdxAry[ i ] |= nFontTag;
2277             }
2278             rPos += maDrawBase;
2279             rPos += maDrawOffset;
2280             return nRetVal;
2281         }
2282     }
2283 
2284     // #111016# reset to base level font when done
2285     mpLayouts[0]->InitFont();
2286     return 0;
2287 }
2288 
2289 // -----------------------------------------------------------------------
2290 
GetOutline(SalGraphics & rGraphics,::basegfx::B2DPolyPolygonVector & rPPV) const2291 bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
2292     ::basegfx::B2DPolyPolygonVector& rPPV ) const
2293 {
2294     bool bRet = false;
2295 
2296     for( int i = mnLevel; --i >= 0; )
2297     {
2298         SalLayout& rLayout = *mpLayouts[ i ];
2299         rLayout.DrawBase() = maDrawBase;
2300         rLayout.DrawOffset() += maDrawOffset;
2301         rLayout.InitFont();
2302         bRet |= rLayout.GetOutline( rGraphics, rPPV );
2303         rLayout.DrawOffset() -= maDrawOffset;
2304     }
2305 
2306     return bRet;
2307 }
2308 
2309 // -----------------------------------------------------------------------
2310 
GetBoundRect(SalGraphics & rGraphics,Rectangle & rRect) const2311 bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
2312 {
2313     bool bRet = false;
2314 
2315     Rectangle aRectangle;
2316     for( int i = mnLevel; --i >= 0; )
2317     {
2318         SalLayout& rLayout = *mpLayouts[ i ];
2319         rLayout.DrawBase() = maDrawBase;
2320         rLayout.DrawOffset() += maDrawOffset;
2321         rLayout.InitFont();
2322         if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
2323         {
2324             rRect.Union( aRectangle );
2325             bRet = true;
2326         }
2327         rLayout.DrawOffset() -= maDrawOffset;
2328     }
2329 
2330     return bRet;
2331 }
2332 
2333 // =======================================================================
2334