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