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