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