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