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