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