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