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 5842 : bool IsDiacritic( sal_UCS4 nChar )
75 : {
76 : // shortcut abvious non-diacritics
77 5842 : if( nChar < 0x0300 )
78 0 : return false;
79 5842 : 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 5842 : const DiaRange* pRange = &aRanges[0];
97 79786 : for( int i = nCount; --i >= 0; ++pRange )
98 76996 : if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
99 3052 : return true;
100 :
101 2790 : 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 23639 : sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
152 : {
153 : // currently only conversion from ASCII digits is interesting
154 23639 : 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 23639 : switch( eLang & LANGUAGE_MASK_PRIMARY )
162 : {
163 : default:
164 23639 : nOffset = 0;
165 23639 : 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 23639 : nChar += nOffset;
233 23639 : return nChar;
234 : }
235 :
236 : // -----------------------------------------------------------------------
237 :
238 506760 : inline bool IsControlChar( sal_UCS4 cChar )
239 : {
240 : // C0 control characters
241 506760 : if( (0x0001 <= cChar) && (cChar <= 0x001F) )
242 4 : return true;
243 : // formatting characters
244 506756 : if( (0x200E <= cChar) && (cChar <= 0x200F) )
245 0 : return true;
246 506756 : if( (0x2028 <= cChar) && (cChar <= 0x202E) )
247 0 : return true;
248 : // deprecated formatting characters
249 506756 : if( (0x206A <= cChar) && (cChar <= 0x206F) )
250 0 : return true;
251 506756 : if( (0x2060 == cChar) )
252 0 : return true;
253 : // byte order markers and invalid unicode
254 506756 : if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
255 0 : return true;
256 506756 : return false;
257 : }
258 :
259 : // =======================================================================
260 :
261 14000 : bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
262 : {
263 : // check if charpos could extend current run
264 14000 : int nIndex = maRuns.size();
265 14000 : if( nIndex >= 2 )
266 : {
267 620 : int nRunPos0 = maRuns[ nIndex-2 ];
268 620 : int nRunPos1 = maRuns[ nIndex-1 ];
269 620 : if( ((nCharPos + bRTL) == nRunPos1)
270 : && ((nRunPos0 > nRunPos1) == bRTL) )
271 : {
272 : // extend current run by new charpos
273 592 : maRuns[ nIndex-1 ] = nCharPos + !bRTL;
274 592 : return false;
275 : }
276 : // ignore new charpos when it is in current run
277 28 : if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
278 0 : return false;
279 28 : if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
280 0 : return false;
281 : }
282 :
283 : // else append a new run consisting of the new charpos
284 13408 : maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
285 13408 : maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
286 13408 : return true;
287 : }
288 :
289 : // -----------------------------------------------------------------------
290 :
291 114095 : bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
292 : {
293 114095 : if( nCharPos0 == nCharPos1 )
294 2 : return false;
295 :
296 : // swap if needed
297 114093 : if( bRTL == (nCharPos0 < nCharPos1) )
298 : {
299 16 : int nTemp = nCharPos0;
300 16 : nCharPos0 = nCharPos1;
301 16 : nCharPos1 = nTemp;
302 : }
303 :
304 : // append new run
305 114093 : maRuns.push_back( nCharPos0 );
306 114093 : maRuns.push_back( nCharPos1 );
307 114093 : return true;
308 : }
309 :
310 : // -----------------------------------------------------------------------
311 :
312 14098 : bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
313 : {
314 14098 : if( mnRunIndex >= (int)maRuns.size() )
315 14 : return false;
316 :
317 14084 : int nMinCharPos = maRuns[ mnRunIndex+0 ];
318 14084 : int nEndCharPos = maRuns[ mnRunIndex+1 ];
319 14084 : if( nMinCharPos > nEndCharPos ) // reversed in RTL case
320 : {
321 336 : int nTemp = nMinCharPos;
322 336 : nMinCharPos = nEndCharPos;
323 336 : nEndCharPos = nTemp;
324 : }
325 :
326 14084 : if( nCharPos < nMinCharPos )
327 56 : return false;
328 14028 : if( nCharPos >= nEndCharPos )
329 14 : return false;
330 14014 : return true;
331 : }
332 :
333 20422 : bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
334 : {
335 20422 : bool bRet = false;
336 20422 : int nRunIndex = mnRunIndex;
337 :
338 20422 : ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
339 :
340 20422 : pThis->ResetPos();
341 :
342 20464 : for (size_t i = 0; i < maRuns.size(); i+=2)
343 : {
344 6746 : if( (bRet = PosIsInRun( nCharPos )) == true )
345 6704 : break;
346 42 : pThis->NextRun();
347 : }
348 :
349 20422 : pThis->mnRunIndex = nRunIndex;
350 20422 : return bRet;
351 : }
352 :
353 :
354 : // -----------------------------------------------------------------------
355 :
356 34078 : bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
357 : {
358 : // negative nCharPos => reset to first run
359 34078 : if( *nCharPos < 0 )
360 8169 : mnRunIndex = 0;
361 :
362 : // return false when all runs completed
363 34078 : if( mnRunIndex >= (int)maRuns.size() )
364 0 : return false;
365 :
366 34078 : int nRunPos0 = maRuns[ mnRunIndex+0 ];
367 34078 : int nRunPos1 = maRuns[ mnRunIndex+1 ];
368 34078 : *bRightToLeft = (nRunPos0 > nRunPos1);
369 :
370 34078 : if( *nCharPos < 0 )
371 : {
372 : // get first valid nCharPos in run
373 8169 : *nCharPos = nRunPos0;
374 : }
375 : else
376 : {
377 : // advance to next nCharPos for LTR case
378 25909 : if( !*bRightToLeft )
379 25671 : ++(*nCharPos);
380 :
381 : // advance to next run if current run is completed
382 25909 : if( *nCharPos == nRunPos1 )
383 : {
384 8189 : if( (mnRunIndex += 2) >= (int)maRuns.size() )
385 8167 : return false;
386 22 : nRunPos0 = maRuns[ mnRunIndex+0 ];
387 22 : nRunPos1 = maRuns[ mnRunIndex+1 ];
388 22 : *bRightToLeft = (nRunPos0 > nRunPos1);
389 22 : *nCharPos = nRunPos0;
390 : }
391 : }
392 :
393 : // advance to next nCharPos for RTL case
394 25911 : if( *bRightToLeft )
395 238 : --(*nCharPos);
396 :
397 25911 : return true;
398 : }
399 :
400 : // -----------------------------------------------------------------------
401 :
402 292928 : bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
403 : {
404 292928 : if( mnRunIndex >= (int)maRuns.size() )
405 143097 : return false;
406 :
407 149831 : int nRunPos0 = maRuns[ mnRunIndex+0 ];
408 149831 : int nRunPos1 = maRuns[ mnRunIndex+1 ];
409 149831 : *bRightToLeft = (nRunPos1 < nRunPos0) ;
410 149831 : if( !*bRightToLeft )
411 : {
412 149703 : *nMinRunPos = nRunPos0;
413 149703 : *nEndRunPos = nRunPos1;
414 : }
415 : else
416 : {
417 128 : *nMinRunPos = nRunPos1;
418 128 : *nEndRunPos = nRunPos0;
419 : }
420 149831 : return true;
421 : }
422 :
423 : // =======================================================================
424 :
425 114091 : 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 114091 : mnOrientation( 0 )
436 : {
437 114091 : 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 114089 : bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
445 114089 : AddRun( mnMinCharPos, mnEndCharPos, bRTL );
446 : }
447 : else
448 : {
449 : // handle weak BiDi mode
450 :
451 2 : UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
452 2 : 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 2 : UErrorCode rcI18n = U_ZERO_ERROR;
458 2 : UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
459 2 : if( !pParaBidi )
460 114091 : return;
461 2 : ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n ); // UChar != sal_Unicode in MinGW
462 :
463 2 : UBiDi* pLineBidi = pParaBidi;
464 2 : int nSubLength = mnEndCharPos - mnMinCharPos;
465 2 : 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 2 : const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
473 : //maRuns.resize( 2 * nRunCount );
474 4 : for( int i = 0; i < nRunCount; ++i )
475 : {
476 : int32_t nMinPos, nLength;
477 2 : const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
478 2 : const int nPos0 = nMinPos + mnMinCharPos;
479 2 : const int nPos1 = nPos0 + nLength;
480 :
481 2 : const bool bRTL = (nDir == UBIDI_RTL);
482 2 : AddRun( nPos0, nPos1, bRTL );
483 : }
484 :
485 : // cleanup BiDi engine
486 2 : if( pLineBidi != pParaBidi )
487 0 : ubidi_close( pLineBidi );
488 2 : ubidi_close( pParaBidi );
489 : }
490 :
491 : // prepare calls to GetNextPos/GetNextRun
492 114091 : maRuns.ResetPos();
493 : }
494 :
495 : // -----------------------------------------------------------------------
496 :
497 : // add a run after splitting it up to get rid of control chars
498 114091 : 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 114091 : if( !bRTL )
504 : {
505 620687 : for( int i = nCharPos0; i < nCharPos1; ++i )
506 506612 : if( IsControlChar( mpStr[i] ) )
507 : {
508 : // add run until control char
509 4 : maRuns.AddRun( nCharPos0, i, bRTL );
510 4 : nCharPos0 = i + 1;
511 : }
512 : }
513 : else
514 : {
515 180 : for( int i = nCharPos1; --i >= nCharPos0; )
516 148 : 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 114091 : maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
526 114091 : }
527 :
528 : // -----------------------------------------------------------------------
529 :
530 13380 : bool ImplLayoutArgs::PrepareFallback()
531 : {
532 : // short circuit if no fallback is needed
533 13380 : if( maReruns.IsEmpty() )
534 : {
535 6690 : maRuns.Clear();
536 6690 : 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 6690 : IntVector aPosVector;
546 6690 : aPosVector.reserve( mnLength );
547 6690 : maReruns.ResetPos();
548 13394 : for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() )
549 13704 : for( int i = nMin; i < nEnd; ++i )
550 7000 : aPosVector.push_back( i );
551 6690 : maReruns.Clear();
552 :
553 : // sort the individual fallback requests
554 6690 : std::sort( aPosVector.begin(), aPosVector.end() );
555 :
556 : // adjust fallback runs to have the same order and limits of the original runs
557 6690 : ImplLayoutRuns aNewRuns;
558 6690 : maRuns.ResetPos();
559 13380 : for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
560 : {
561 6690 : if( !bRTL) {
562 6676 : IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
563 13550 : for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
564 6874 : aNewRuns.AddPos( *it, bRTL );
565 : } else {
566 14 : IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
567 154 : while( (it != aPosVector.begin()) && (*--it >= nMin) )
568 126 : aNewRuns.AddPos( *it, bRTL );
569 : }
570 : }
571 :
572 6690 : maRuns = aNewRuns; // TODO: use vector<>::swap()
573 6690 : maRuns.ResetPos();
574 6690 : return true;
575 : }
576 :
577 : // -----------------------------------------------------------------------
578 :
579 259450 : bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
580 : {
581 259450 : bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
582 259450 : maRuns.NextRun();
583 259450 : return bValid;
584 : }
585 :
586 : // =======================================================================
587 :
588 129717 : SalLayout::SalLayout()
589 : : mnMinCharPos( -1 ),
590 : mnEndCharPos( -1 ),
591 : mnLayoutFlags( 0 ),
592 : mnUnitsPerPixel( 1 ),
593 : mnOrientation( 0 ),
594 : mnRefCount( 1 ),
595 129717 : maDrawOffset( 0, 0 )
596 129717 : {}
597 :
598 : // -----------------------------------------------------------------------
599 :
600 129717 : SalLayout::~SalLayout()
601 129717 : {}
602 :
603 : // -----------------------------------------------------------------------
604 :
605 134965 : void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
606 : {
607 134965 : mnMinCharPos = rArgs.mnMinCharPos;
608 134965 : mnEndCharPos = rArgs.mnEndCharPos;
609 134965 : mnLayoutFlags = rArgs.mnFlags;
610 134965 : mnOrientation = rArgs.mnOrientation;
611 134965 : }
612 :
613 : // -----------------------------------------------------------------------
614 :
615 129717 : void SalLayout::Release() const
616 : {
617 : // TODO: protect when multiple threads can access this
618 129717 : if( --mnRefCount > 0 )
619 129717 : return;
620 : // const_cast because some compilers violate ANSI C++ spec
621 129717 : delete const_cast<SalLayout*>(this);
622 : }
623 :
624 : // -----------------------------------------------------------------------
625 :
626 150739 : Point SalLayout::GetDrawPosition( const Point& rRelative ) const
627 : {
628 150739 : Point aPos = maDrawBase;
629 150739 : Point aOfs = rRelative + maDrawOffset;
630 :
631 150739 : if( mnOrientation == 0 )
632 124504 : aPos += aOfs;
633 : else
634 : {
635 : // cache trigonometric results
636 : static int nOldOrientation = 0;
637 : static double fCos = 1.0, fSin = 0.0;
638 26235 : if( nOldOrientation != mnOrientation )
639 : {
640 18 : nOldOrientation = mnOrientation;
641 18 : double fRad = mnOrientation * (M_PI / 1800.0);
642 18 : fCos = cos( fRad );
643 18 : fSin = sin( fRad );
644 : }
645 :
646 26235 : double fX = aOfs.X();
647 26235 : double fY = aOfs.Y();
648 26235 : long nX = static_cast<long>( +fCos * fX + fSin * fY );
649 26235 : long nY = static_cast<long>( +fCos * fY - fSin * fX );
650 26235 : aPos += Point( nX, nY );
651 : }
652 :
653 150739 : 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 330 : bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
702 : ::basegfx::B2DPolyPolygonVector& rVector ) const
703 : {
704 330 : bool bAllOk = true;
705 330 : bool bOneOk = false;
706 :
707 330 : Point aPos;
708 330 : ::basegfx::B2DPolyPolygon aGlyphOutline;
709 568 : for( int nStart = 0;;)
710 : {
711 : sal_GlyphId nLGlyph;
712 568 : if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
713 : break;
714 :
715 : // get outline of individual glyph, ignoring "empty" glyphs
716 238 : bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
717 238 : bAllOk &= bSuccess;
718 238 : bOneOk |= bSuccess;
719 : // only add non-empty outlines
720 238 : if( bSuccess && (aGlyphOutline.count() > 0) )
721 : {
722 146 : 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 146 : rVector.push_back( aGlyphOutline );
729 : }
730 : }
731 :
732 330 : return (bAllOk && bOneOk);
733 : }
734 :
735 : // -----------------------------------------------------------------------
736 :
737 38226 : bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
738 : {
739 38226 : bool bRet = false;
740 38226 : rRect.SetEmpty();
741 :
742 38226 : Point aPos;
743 38226 : Rectangle aRectangle;
744 87774 : for( int nStart = 0;;)
745 : {
746 : sal_GlyphId nLGlyph;
747 87774 : if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
748 : break;
749 :
750 : // get bounding rectangle of individual glyph
751 49548 : if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
752 : {
753 : // merge rectangle
754 49548 : aRectangle += aPos;
755 49548 : if (rRect.IsEmpty())
756 36562 : rRect = aRectangle;
757 : else
758 12986 : rRect.Union(aRectangle);
759 49548 : bRet = true;
760 : }
761 : }
762 :
763 38226 : 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 123027 : GenericSalLayout::GenericSalLayout()
794 123027 : {}
795 :
796 : // -----------------------------------------------------------------------
797 :
798 123027 : GenericSalLayout::~GenericSalLayout()
799 123027 : {}
800 :
801 : // -----------------------------------------------------------------------
802 :
803 515976 : void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
804 : {
805 515976 : m_GlyphItems.push_back(rGlyphItem);
806 515976 : }
807 :
808 : // -----------------------------------------------------------------------
809 :
810 27111 : bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
811 : {
812 : // initialize character extents buffer
813 27111 : int nCharCount = mnEndCharPos - mnMinCharPos;
814 239200 : for( int n = 0; n < nCharCount; ++n )
815 212089 : pCharWidths[n] = 0;
816 :
817 : // determine cluster extents
818 238880 : 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 211769 : if( !pG->IsClusterStart() )
822 0 : continue;
823 :
824 211769 : int n = pG->mnCharPos;
825 211769 : if( n >= mnEndCharPos )
826 0 : continue;
827 211769 : n -= mnMinCharPos;
828 211769 : if( n < 0 )
829 0 : continue;
830 :
831 : // left glyph in cluster defines default extent
832 211769 : long nXPosMin = pG->maLinearPos.X();
833 211769 : 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 423538 : 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 423538 : for( GlyphVector::const_iterator pN = pG; ++pN != end; )
861 : {
862 184898 : if( pN->IsClusterStart() )
863 184898 : 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 211769 : if( nXPosMax < nXPosMin )
870 0 : nXPosMin = nXPosMax = 0;
871 :
872 : // character width is sum of glyph cluster widths
873 211769 : 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 27111 : return true;
882 : }
883 :
884 : // -----------------------------------------------------------------------
885 :
886 64009 : long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
887 : {
888 64009 : if( pCharWidths )
889 26746 : if( !GetCharWidths( pCharWidths ) )
890 0 : return 0;
891 :
892 64009 : long nWidth = GetTextWidth();
893 64009 : return nWidth;
894 : }
895 :
896 : // -----------------------------------------------------------------------
897 :
898 : // the text width is the maximum logical extent of all glyphs
899 68342 : long GenericSalLayout::GetTextWidth() const
900 : {
901 68342 : if( m_GlyphItems.empty() )
902 4328 : return 0;
903 :
904 : // initialize the extent
905 64014 : long nMinPos = 0;
906 64014 : long nMaxPos = 0;
907 :
908 448473 : 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 384459 : long nXPos = pG->maLinearPos.X();
912 384459 : if( nMinPos > nXPos )
913 0 : nMinPos = nXPos;
914 384459 : nXPos += pG->mnNewWidth;
915 384459 : if( nMaxPos < nXPos )
916 383555 : nMaxPos = nXPos;
917 : }
918 :
919 64014 : long nWidth = nMaxPos - nMinPos;
920 64014 : return nWidth;
921 : }
922 :
923 : // -----------------------------------------------------------------------
924 :
925 127399 : void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
926 : {
927 127399 : SalLayout::AdjustLayout( rArgs );
928 :
929 127399 : if( rArgs.mpDXArray )
930 1407 : ApplyDXArray( rArgs );
931 125992 : else if( rArgs.mnLayoutWidth )
932 4128 : Justify( rArgs.mnLayoutWidth );
933 127399 : }
934 :
935 : // -----------------------------------------------------------------------
936 :
937 1407 : void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
938 : {
939 1407 : if( m_GlyphItems.empty())
940 : return;
941 :
942 : // determine cluster boundaries and x base offset
943 1407 : const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
944 1407 : int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
945 : size_t i;
946 : int n,p;
947 1407 : long nBasePointX = -1;
948 1407 : if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
949 196 : nBasePointX = 0;
950 20254 : for(p = 0; p < nCharCount; ++p )
951 18847 : pLogCluster[ p ] = -1;
952 :
953 20246 : for( i = 0; i < m_GlyphItems.size(); ++i)
954 : {
955 18839 : n = m_GlyphItems[i].mnCharPos - rArgs.mnMinCharPos;
956 18839 : if( (n < 0) || (nCharCount <= n) )
957 0 : continue;
958 18839 : if( pLogCluster[ n ] < 0 )
959 18839 : pLogCluster[ n ] = i;
960 18839 : if( nBasePointX < 0 )
961 1211 : 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 1407 : for( n = 0; n < nCharCount; ++n )
966 1407 : if( (p = pLogCluster[0]) >= 0 )
967 1407 : break;
968 1407 : if( n >= nCharCount )
969 : return;
970 20254 : for( n = 0; n < nCharCount; ++n )
971 : {
972 18847 : if( pLogCluster[ n ] < 0 )
973 8 : pLogCluster[ n ] = p;
974 : else
975 18839 : p = pLogCluster[ n ];
976 : }
977 :
978 : // calculate adjusted cluster widths
979 1407 : sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( m_GlyphItems.size() * sizeof(long) );
980 20246 : for( i = 0; i < m_GlyphItems.size(); ++i )
981 18839 : pNewGlyphWidths[ i ] = 0;
982 :
983 : bool bRTL;
984 21653 : for( int nCharPos = p = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
985 : {
986 18839 : n = nCharPos - rArgs.mnMinCharPos;
987 18839 : if( (n < 0) || (nCharCount <= n) ) continue;
988 :
989 18839 : if( pLogCluster[ n ] >= 0 )
990 18839 : p = pLogCluster[ n ];
991 18839 : if( p >= 0 )
992 : {
993 18839 : long nDelta = rArgs.mpDXArray[ n ] ;
994 18839 : if( n > 0 )
995 17432 : nDelta -= rArgs.mpDXArray[ n-1 ];
996 18839 : pNewGlyphWidths[ p ] += nDelta * mnUnitsPerPixel;
997 : }
998 : }
999 :
1000 : // move cluster positions using the adjusted widths
1001 1407 : long nDelta = 0;
1002 1407 : long nNewPos = 0;
1003 20246 : for( i = 0; i < m_GlyphItems.size(); ++i)
1004 : {
1005 18839 : if( m_GlyphItems[i].IsClusterStart() )
1006 : {
1007 : // calculate original and adjusted cluster width
1008 18839 : int nOldClusterWidth = m_GlyphItems[i].mnNewWidth;
1009 18839 : int nNewClusterWidth = pNewGlyphWidths[i];
1010 : size_t j;
1011 37678 : for( j = i; ++j < m_GlyphItems.size(); )
1012 : {
1013 17432 : if( m_GlyphItems[j].IsClusterStart() )
1014 17432 : 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 18839 : const int nDiff = nNewClusterWidth - nOldClusterWidth;
1020 :
1021 : // adjust cluster glyph widths and positions
1022 18839 : nDelta = nBasePointX + (nNewPos - m_GlyphItems[i].maLinearPos.X());
1023 18839 : if( !m_GlyphItems[i].IsRTLGlyph() )
1024 : {
1025 : // for LTR case extend rightmost glyph in cluster
1026 18727 : m_GlyphItems[j - 1].mnNewWidth += nDiff;
1027 : }
1028 : else
1029 : {
1030 : // right align cluster in new space for RTL case
1031 112 : m_GlyphItems[i].mnNewWidth += nDiff;
1032 112 : nDelta += nDiff;
1033 : }
1034 :
1035 18839 : nNewPos += nNewClusterWidth;
1036 : }
1037 :
1038 18839 : m_GlyphItems[i].maLinearPos.X() += nDelta;
1039 : }
1040 : }
1041 :
1042 : // -----------------------------------------------------------------------
1043 :
1044 4128 : void GenericSalLayout::Justify( long nNewWidth )
1045 : {
1046 4128 : nNewWidth *= mnUnitsPerPixel;
1047 4128 : int nOldWidth = GetTextWidth();
1048 4128 : if( !nOldWidth || nNewWidth==nOldWidth )
1049 : return;
1050 :
1051 826 : if(m_GlyphItems.empty())
1052 : {
1053 : return;
1054 : }
1055 : // find rightmost glyph, it won't get stretched
1056 826 : GlyphVector::iterator pGRight = m_GlyphItems.begin();
1057 826 : pGRight += m_GlyphItems.size() - 1;
1058 826 : GlyphVector::iterator pG;
1059 : // count stretchable glyphs
1060 826 : int nStretchable = 0;
1061 826 : int nMaxGlyphWidth = 0;
1062 1060 : for(pG = m_GlyphItems.begin(); pG != pGRight; ++pG)
1063 : {
1064 234 : if( !pG->IsDiacritic() )
1065 234 : ++nStretchable;
1066 234 : if( nMaxGlyphWidth < pG->mnOrigWidth )
1067 126 : nMaxGlyphWidth = pG->mnOrigWidth;
1068 : }
1069 :
1070 : // move rightmost glyph to requested position
1071 826 : nOldWidth -= pGRight->mnOrigWidth;
1072 826 : if( nOldWidth <= 0 )
1073 : return;
1074 122 : if( nNewWidth < nMaxGlyphWidth)
1075 0 : nNewWidth = nMaxGlyphWidth;
1076 122 : nNewWidth -= pGRight->mnOrigWidth;
1077 122 : pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
1078 :
1079 : // justify glyph widths and positions
1080 122 : int nDiffWidth = nNewWidth - nOldWidth;
1081 122 : if( nDiffWidth >= 0) // expanded case
1082 : {
1083 : // expand width by distributing space between glyphs evenly
1084 58 : int nDeltaSum = 0;
1085 170 : for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
1086 : {
1087 : // move glyph to justified position
1088 112 : pG->maLinearPos.X() += nDeltaSum;
1089 :
1090 : // do not stretch non-stretchable glyphs
1091 112 : if( pG->IsDiacritic() || (nStretchable <= 0) )
1092 0 : continue;
1093 :
1094 : // distribute extra space equally to stretchable glyphs
1095 112 : int nDeltaWidth = nDiffWidth / nStretchable--;
1096 112 : nDiffWidth -= nDeltaWidth;
1097 112 : pG->mnNewWidth += nDeltaWidth;
1098 112 : nDeltaSum += nDeltaWidth;
1099 : }
1100 : }
1101 : else // condensed case
1102 : {
1103 : // squeeze width by moving glyphs proportionally
1104 64 : double fSqueeze = (double)nNewWidth / nOldWidth;
1105 64 : if(m_GlyphItems.size() > 1)
1106 : {
1107 186 : for( pG = m_GlyphItems.begin(); ++pG != pGRight;)
1108 : {
1109 58 : int nX = pG->maLinearPos.X() - maBasePoint.X();
1110 58 : nX = (int)(nX * fSqueeze);
1111 58 : pG->maLinearPos.X() = nX + maBasePoint.X();
1112 : }
1113 : }
1114 : // adjust glyph widths to new positions
1115 186 : for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
1116 122 : 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 161 : void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
1218 : {
1219 : // initialize result array
1220 161 : long nXPos = -1;
1221 : int i;
1222 2077 : for( i = 0; i < nMaxIndex; ++i )
1223 1916 : pCaretXArray[ i ] = nXPos;
1224 :
1225 : // calculate caret positions using glyph array
1226 1119 : for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1227 : {
1228 958 : nXPos = pG->maLinearPos.X();
1229 958 : long nXRight = nXPos + pG->mnOrigWidth;
1230 958 : int n = pG->mnCharPos;
1231 958 : int nCurrIdx = 2 * (n - mnMinCharPos);
1232 958 : if( !pG->IsRTLGlyph() )
1233 : {
1234 : // normal positions for LTR case
1235 958 : pCaretXArray[ nCurrIdx ] = nXPos;
1236 958 : 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 161 : }
1246 :
1247 : // -----------------------------------------------------------------------
1248 :
1249 365 : int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
1250 : {
1251 365 : int nCharCapacity = mnEndCharPos - mnMinCharPos;
1252 365 : sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
1253 365 : if( !GetCharWidths( pCharWidths ) )
1254 0 : return STRING_LEN;
1255 :
1256 365 : long nWidth = 0;
1257 15493 : for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1258 : {
1259 15382 : nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
1260 15382 : if( nWidth > nMaxWidth )
1261 254 : return i;
1262 15128 : nWidth += nCharExtra;
1263 : }
1264 :
1265 111 : return STRING_LEN;
1266 : }
1267 :
1268 : // -----------------------------------------------------------------------
1269 :
1270 191403 : int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1271 : int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
1272 : {
1273 191403 : GlyphVector::const_iterator pG = m_GlyphItems.begin();
1274 191403 : GlyphVector::const_iterator pGEnd = m_GlyphItems.end();
1275 191403 : pG += nStart;
1276 :
1277 : // find next glyph in substring
1278 191403 : for(; pG != pGEnd; ++nStart, ++pG )
1279 : {
1280 114159 : int n = pG->mnCharPos;
1281 114159 : if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
1282 114159 : break;
1283 : }
1284 :
1285 : // return zero if no more glyph found
1286 191403 : if( nStart >= (int)m_GlyphItems.size() )
1287 77244 : return 0;
1288 :
1289 : // calculate absolute position in pixel units
1290 114159 : Point aRelativePos = pG->maLinearPos - maBasePoint;
1291 :
1292 : // find more glyphs which can be merged into one drawing instruction
1293 114159 : int nCount = 0;
1294 114159 : long nYPos = pG->maLinearPos.Y();
1295 114159 : long nOldFlags = pG->mnGlyphIndex;
1296 0 : for(;;)
1297 : {
1298 : // update return data with glyph info
1299 114159 : ++nCount;
1300 114159 : *(pGlyphs++) = pG->mnGlyphIndex;
1301 114159 : if( pCharPosAry )
1302 14014 : *(pCharPosAry++) = pG->mnCharPos;
1303 114159 : if( pGlyphAdvAry )
1304 14014 : *pGlyphAdvAry = pG->mnNewWidth;
1305 :
1306 : // break at end of glyph list
1307 114159 : if( ++nStart >= (int)m_GlyphItems.size() )
1308 74826 : break;
1309 : // break when enough glyphs
1310 39333 : if( nCount >= nLen )
1311 39333 : 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 114159 : aRelativePos.X() /= mnUnitsPerPixel;
1346 114159 : aRelativePos.Y() /= mnUnitsPerPixel;
1347 114159 : rPos = GetDrawPosition( aRelativePos );
1348 :
1349 114159 : return nCount;
1350 : }
1351 :
1352 : // -----------------------------------------------------------------------
1353 :
1354 6718 : void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
1355 : {
1356 6718 : if( nStart >= (int)m_GlyphItems.size() )
1357 6718 : return;
1358 :
1359 6718 : GlyphVector::iterator pG = m_GlyphItems.begin();
1360 6718 : 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 6718 : if( pG->IsRTLGlyph() )
1366 42 : nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
1367 : // calculate the x-offset to the old position
1368 6718 : long nXDelta = nNewXPos - pG->maLinearPos.X();
1369 : // adjust all following glyph positions if needed
1370 6718 : if( nXDelta != 0 )
1371 : {
1372 170 : for( GlyphVector::iterator pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1373 : {
1374 142 : pG->maLinearPos.X() += nXDelta;
1375 : }
1376 : }
1377 : }
1378 :
1379 : // -----------------------------------------------------------------------
1380 :
1381 7000 : void GenericSalLayout::DropGlyph( int nStart )
1382 : {
1383 7000 : if( nStart >= (int)m_GlyphItems.size())
1384 7000 : return;
1385 :
1386 7000 : GlyphVector::iterator pG = m_GlyphItems.begin();
1387 7000 : pG += nStart;
1388 7000 : pG->mnGlyphIndex = GF_DROPPED;
1389 7000 : pG->mnCharPos = -1;
1390 : }
1391 :
1392 : // -----------------------------------------------------------------------
1393 :
1394 13380 : void GenericSalLayout::Simplify( bool bIsBase )
1395 : {
1396 13380 : const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
1397 :
1398 : // remove dropped glyphs inplace
1399 13380 : size_t j = 0;
1400 27394 : for(size_t i = 0; i < m_GlyphItems.size(); i++ )
1401 : {
1402 14014 : if( m_GlyphItems[i].mnGlyphIndex == nDropMarker )
1403 7000 : continue;
1404 :
1405 7014 : if( i != j )
1406 : {
1407 14 : m_GlyphItems[j] = m_GlyphItems[i];
1408 : }
1409 7014 : j += 1;
1410 : }
1411 13380 : m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
1412 13380 : }
1413 :
1414 : // -----------------------------------------------------------------------
1415 :
1416 : // make sure GlyphItems are sorted left to right
1417 123027 : 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 639003 : for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1423 : {
1424 : // find a cluster starting with a diacritic
1425 515976 : if( !pG->IsDiacritic() )
1426 512924 : continue;
1427 3052 : if( !pG->IsClusterStart() )
1428 0 : continue;
1429 6104 : 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 123027 : }
1450 :
1451 : // =======================================================================
1452 :
1453 6690 : MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const PhysicalFontFace* pBaseFont )
1454 : : SalLayout()
1455 : , mnLevel( 1 )
1456 6690 : , mbInComplete( false )
1457 : {
1458 : //maFallbackRuns[0].Clear();
1459 6690 : mpFallbackFonts[ 0 ] = pBaseFont;
1460 6690 : mpLayouts[ 0 ] = &rBaseLayout;
1461 6690 : mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
1462 6690 : }
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 133800 : MultiSalLayout::~MultiSalLayout()
1473 : {
1474 20070 : for( int i = 0; i < mnLevel; ++i )
1475 13380 : mpLayouts[ i ]->Release();
1476 127110 : }
1477 :
1478 : // -----------------------------------------------------------------------
1479 :
1480 6690 : bool MultiSalLayout::AddFallback( SalLayout& rFallback,
1481 : ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
1482 : {
1483 6690 : if( mnLevel >= MAX_FALLBACK )
1484 0 : return false;
1485 :
1486 6690 : mpFallbackFonts[ mnLevel ] = pFallbackFont;
1487 6690 : mpLayouts[ mnLevel ] = &rFallback;
1488 6690 : maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1489 6690 : ++mnLevel;
1490 6690 : return true;
1491 : }
1492 :
1493 : // -----------------------------------------------------------------------
1494 :
1495 6690 : bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
1496 : {
1497 6690 : if( mnLevel <= 1 )
1498 0 : return false;
1499 6690 : if (!mbInComplete)
1500 6690 : maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1501 6690 : return true;
1502 : }
1503 :
1504 : // -----------------------------------------------------------------------
1505 :
1506 6690 : void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1507 : {
1508 6690 : SalLayout::AdjustLayout( rArgs );
1509 6690 : ImplLayoutArgs aMultiArgs = rArgs;
1510 :
1511 6690 : 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 438 : long nTargetWidth = aMultiArgs.mnLayoutWidth;
1516 438 : nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1517 438 : aMultiArgs.mnLayoutWidth = 0;
1518 :
1519 : // we need to get the original unmodified layouts ready
1520 1314 : for( int n = 0; n < mnLevel; ++n )
1521 876 : mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1522 : // then we can measure the unmodified metrics
1523 438 : int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1524 438 : sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
1525 438 : 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 438 : long nOrigWidth = 0;
1530 438 : int nStretchable = 0;
1531 876 : for( int i = 0; i < nCharCount; ++i )
1532 : {
1533 : // convert array from widths to sum of widths
1534 438 : nOrigWidth += pJustificationArray[i];
1535 438 : if( pJustificationArray[i] > 0 )
1536 438 : ++nStretchable;
1537 : }
1538 :
1539 : // now we are able to distribute the extra width over the virtual char widths
1540 438 : if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1541 : {
1542 92 : int nDiffWidth = nTargetWidth - nOrigWidth;
1543 92 : int nWidthSum = 0;
1544 184 : for( int i = 0; i < nCharCount; ++i )
1545 : {
1546 92 : int nJustWidth = pJustificationArray[i];
1547 92 : if( (nJustWidth > 0) && (nStretchable > 0) )
1548 : {
1549 92 : int nDeltaWidth = nDiffWidth / nStretchable;
1550 92 : nJustWidth += nDeltaWidth;
1551 92 : nDiffWidth -= nDeltaWidth;
1552 92 : --nStretchable;
1553 : }
1554 92 : nWidthSum += nJustWidth;
1555 92 : pJustificationArray[i] = nWidthSum;
1556 : }
1557 92 : 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 92 : 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 92 : 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 6690 : std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1580 6690 : rArgs.ResetPos();
1581 : bool bRtl;
1582 : int nRunStart, nRunEnd;
1583 20070 : while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1584 : {
1585 6690 : if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1586 14 : vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1587 : }
1588 6690 : 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 6690 : int nValid[ MAX_FALLBACK ] = {0};
1596 :
1597 : sal_GlyphId nDummy;
1598 6690 : Point aPos;
1599 6690 : int nLevel = 0, n;
1600 20070 : for( n = 0; n < mnLevel; ++n )
1601 : {
1602 : // now adjust the individual components
1603 13380 : if( n > 0 )
1604 : {
1605 6690 : aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1606 6690 : aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
1607 : }
1608 13380 : mpLayouts[n]->AdjustLayout( aMultiArgs );
1609 :
1610 : // disable glyph-injection for glyph-fallback SalLayout iteration
1611 13380 : mpLayouts[n]->DisableGlyphInjection( true );
1612 :
1613 : // remove unused parts of component
1614 13380 : if( n > 0 )
1615 : {
1616 6690 : if (mbInComplete && (n == mnLevel-1))
1617 0 : mpLayouts[n]->Simplify( true );
1618 : else
1619 6690 : mpLayouts[n]->Simplify( false );
1620 : }
1621 :
1622 : // prepare merging components
1623 13380 : nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1624 26760 : nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1625 26760 : 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 13380 : 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 13380 : if( nLevel != n )
1639 : {
1640 0 : mpLayouts[ nLevel ] = mpLayouts[ n ];
1641 0 : mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ];
1642 0 : maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1643 : }
1644 13380 : ++nLevel;
1645 : }
1646 : }
1647 6690 : mnLevel = nLevel;
1648 :
1649 : // merge the fallback levels
1650 6690 : long nXPos = 0;
1651 6690 : double fUnitMul = 1.0;
1652 20070 : for( n = 0; n < nLevel; ++n )
1653 13380 : maFallbackRuns[n].ResetPos();
1654 6690 : int nActiveCharPos = nCharPos[0];
1655 6690 : int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
1656 6690 : rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1657 6690 : int nRunVisibleEndChar = nCharPos[0];
1658 20098 : while( nValid[0] && (nLevel > 0))
1659 : {
1660 : // find best fallback level
1661 13422 : for( n = 0; n < nLevel; ++n )
1662 13422 : if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1663 : // fallback level n wins when it requested no further fallback
1664 6718 : break;
1665 6718 : int nFBLevel = n;
1666 :
1667 6718 : if( n < nLevel )
1668 : {
1669 : // use base(n==0) or fallback(n>=1) level
1670 6718 : fUnitMul = mnUnitsPerPixel;
1671 6718 : fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1672 6718 : long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1673 6718 : 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 6718 : if( n > 0 )
1682 : {
1683 : // drop the NotDef glyphs in the base layout run if a fallback run exists
1684 20718 : while (
1685 7014 : (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
1686 7000 : (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
1687 : )
1688 : {
1689 7000 : mpLayouts[0]->DropGlyph( nStartOld[0] );
1690 7000 : nStartOld[0] = nStartNew[0];
1691 14000 : nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
1692 14000 : 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 7000 : if( !nValid[0] )
1697 6690 : break;
1698 : }
1699 : }
1700 :
1701 : // skip to end of layout run and calculate its advance width
1702 6718 : int nRunAdvance = 0;
1703 6718 : bool bKeepNotDef = (nFBLevel >= nLevel);
1704 296 : for(;;)
1705 : {
1706 7014 : nRunAdvance += nGlyphAdv[n];
1707 :
1708 : // proceed to next glyph
1709 7014 : nStartOld[n] = nStartNew[n];
1710 7014 : int nOrigCharPos = nCharPos[n];
1711 14028 : nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1712 14028 : 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 7014 : if( !nValid[n] )
1718 : {
1719 : // performance optimization (when a fallback layout is no longer needed)
1720 6690 : if( n >= nLevel-1 )
1721 6690 : --nLevel;
1722 6690 : 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 324 : if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
1729 : {
1730 14 : if (nOrigCharPos < nCharPos[n])
1731 : {
1732 0 : if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
1733 0 : break;
1734 : }
1735 14 : else if (nOrigCharPos > nCharPos[n])
1736 : {
1737 14 : if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
1738 0 : break;
1739 : }
1740 : }
1741 :
1742 : // break at end of layout run
1743 324 : if( n > 0 )
1744 : {
1745 : // skip until end of fallback run
1746 310 : if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
1747 14 : break;
1748 : }
1749 : else
1750 : {
1751 : // break when a fallback is needed and available
1752 14 : bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
1753 14 : if( bNeedFallback )
1754 14 : if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
1755 14 : 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 352 : if (aMultiArgs.mpDXArray &&
1763 : nRunVisibleEndChar < mnEndCharPos &&
1764 : nRunVisibleEndChar >= mnMinCharPos &&
1765 28 : nCharPos[n] < mnEndCharPos &&
1766 28 : nCharPos[n] >= mnMinCharPos)
1767 : {
1768 28 : if (vRtl[nActiveCharPos - mnMinCharPos])
1769 : {
1770 56 : if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1771 28 : >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1772 : {
1773 28 : 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 6718 : 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 104 : 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 104 : if (vRtl[nActiveCharPos - mnMinCharPos])
1804 : {
1805 12 : if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1806 8 : nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1807 12 : if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1808 12 : 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 92 : if (nRunVisibleEndChar >= mnMinCharPos)
1816 92 : nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1817 92 : 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 104 : nLastRunEndChar = nRunVisibleEndChar;
1824 104 : nRunVisibleEndChar = nCharPos[0];
1825 : // the requested width is still in pixel units
1826 : // => convert it to base level font units
1827 104 : 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 6614 : if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1834 6604 : nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
1835 : }
1836 :
1837 : // calculate new x position (in base level units)
1838 6718 : nXPos += nRunAdvance;
1839 :
1840 : // prepare for next fallback run
1841 6718 : 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 20140 : for( int i = nFBLevel; --i >= 0;)
1846 : {
1847 6704 : if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1848 : {
1849 6704 : if (bRtl)
1850 : {
1851 28 : if (nRunStart > nActiveCharPos)
1852 14 : maFallbackRuns[i].NextRun();
1853 : }
1854 : else
1855 : {
1856 6676 : if (nRunEnd <= nActiveCharPos)
1857 0 : maFallbackRuns[i].NextRun();
1858 : }
1859 : }
1860 : }
1861 : // if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) )
1862 : // maFallbackRuns[i].NextRun();
1863 : }
1864 :
1865 6690 : mpLayouts[0]->Simplify( true );
1866 :
1867 : // reenable glyph-injection
1868 20070 : for( n = 0; n < mnLevel; ++n )
1869 20070 : mpLayouts[n]->DisableGlyphInjection( false );
1870 6690 : }
1871 :
1872 : // -----------------------------------------------------------------------
1873 :
1874 6690 : void MultiSalLayout::InitFont() const
1875 : {
1876 6690 : if( mnLevel > 0 )
1877 6690 : mpLayouts[0]->InitFont();
1878 6690 : }
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 666 : void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1891 : {
1892 2664 : for( int i = mnLevel; --i >= 0; )
1893 : {
1894 1332 : SalLayout& rLayout = *mpLayouts[ i ];
1895 1332 : rLayout.DrawBase() += maDrawBase;
1896 1332 : rLayout.DrawOffset() += maDrawOffset;
1897 1332 : rLayout.InitFont();
1898 1332 : rLayout.DrawText( rGraphics );
1899 1332 : rLayout.DrawOffset() -= maDrawOffset;
1900 1332 : rLayout.DrawBase() -= maDrawBase;
1901 : }
1902 : // NOTE: now the baselevel font is active again
1903 666 : }
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 4786 : long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
1947 : {
1948 4786 : long nMaxWidth = 0;
1949 :
1950 : // prepare merging of fallback levels
1951 4786 : sal_Int32* pTempWidths = NULL;
1952 4786 : const int nCharCount = mnEndCharPos - mnMinCharPos;
1953 4786 : if( pCharWidths )
1954 : {
1955 1444 : for( int i = 0; i < nCharCount; ++i )
1956 758 : pCharWidths[i] = 0;
1957 686 : pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
1958 : }
1959 :
1960 19144 : for( int n = mnLevel; --n >= 0; )
1961 : {
1962 : // query every fallback level
1963 9572 : long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
1964 9572 : if( !nTextWidth )
1965 5232 : continue;
1966 : // merge results from current level
1967 4340 : double fUnitMul = mnUnitsPerPixel;
1968 4340 : fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1969 4340 : nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
1970 4340 : if( nMaxWidth < nTextWidth )
1971 3882 : nMaxWidth = nTextWidth;
1972 4340 : if( !pCharWidths )
1973 3252 : continue;
1974 : // calculate virtual char widths using most probable fallback layout
1975 2320 : for( int i = 0; i < nCharCount; ++i )
1976 : {
1977 : // #i17359# restriction:
1978 : // one char cannot be resolved from different fallbacks
1979 1232 : if( pCharWidths[i] != 0 )
1980 510 : continue;
1981 722 : long nCharWidth = pTempWidths[i];
1982 722 : if( !nCharWidth )
1983 8 : continue;
1984 714 : nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
1985 714 : pCharWidths[i] = nCharWidth;
1986 : }
1987 : }
1988 :
1989 4786 : 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 92 : bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
2066 : ::basegfx::B2DPolyPolygonVector& rPPV ) const
2067 : {
2068 92 : bool bRet = false;
2069 :
2070 368 : for( int i = mnLevel; --i >= 0; )
2071 : {
2072 184 : SalLayout& rLayout = *mpLayouts[ i ];
2073 184 : rLayout.DrawBase() = maDrawBase;
2074 184 : rLayout.DrawOffset() += maDrawOffset;
2075 184 : rLayout.InitFont();
2076 184 : bRet |= rLayout.GetOutline( rGraphics, rPPV );
2077 184 : rLayout.DrawOffset() -= maDrawOffset;
2078 : }
2079 :
2080 92 : return bRet;
2081 : }
2082 :
2083 : // -----------------------------------------------------------------------
2084 :
2085 1664 : bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
2086 : {
2087 1664 : bool bRet = false;
2088 :
2089 1664 : Rectangle aRectangle;
2090 6656 : for( int i = mnLevel; --i >= 0; )
2091 : {
2092 3328 : SalLayout& rLayout = *mpLayouts[ i ];
2093 3328 : rLayout.DrawBase() = maDrawBase;
2094 3328 : rLayout.DrawOffset() += maDrawOffset;
2095 3328 : rLayout.InitFont();
2096 3328 : if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
2097 : {
2098 1664 : rRect.Union( aRectangle );
2099 1664 : bRet = true;
2100 : }
2101 3328 : rLayout.DrawOffset() -= maDrawOffset;
2102 : }
2103 :
2104 1664 : return bRet;
2105 : }
2106 :
2107 : // =======================================================================
2108 :
2109 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|