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