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