update emoji autocorrect entries from po-files
[LibreOffice.git] / vcl / source / gdi / sallayout.cxx
blob7edfc7ff7ce7a0fde71fe8dc8ef334b478f62921
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <iostream>
21 #include <iomanip>
23 #include "sal/config.h"
25 #include <cstdio>
27 #include <math.h>
28 #include <sal/alloca.h>
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>
36 #include <i18nlangtag/lang.h>
38 #include <tools/debug.hxx>
39 #include <vcl/svapp.hxx>
41 #include <limits.h>
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
52 #include <algorithm>
53 #include <boost/scoped_array.hpp>
55 #ifdef DEBUG
56 //#define MULTI_SL_DEBUG
57 #endif
59 #ifdef MULTI_SL_DEBUG
60 #include <string>
61 FILE * mslLogFile = NULL;
62 FILE * mslLog()
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
74 #endif
76 std::ostream &operator <<(std::ostream& s, ImplLayoutArgs &rArgs)
78 #ifndef SAL_LOG_INFO
79 (void) rArgs;
80 #else
81 s << "ImplLayoutArgs{";
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 << "}";
106 s << ",Length=" << rArgs.mnLength;
107 s << ",MinCharPos=" << rArgs.mnMinCharPos;
108 s << ",EndCharPos=" << rArgs.mnEndCharPos;
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;
124 if (rArgs.mnLength > lim)
125 s << "...";
126 s << "\"";
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 << ",";
140 if (count > lim) {
141 if (count > lim + 1)
142 s << "...";
143 s << rArgs.mpDXArray[count-1];
145 s << "]";
146 } else
147 s << "NULL";
149 s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
151 s << "}";
153 #endif
154 return s;
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 bool IsDiacritic( sal_UCS4 nChar )
161 // shortcut abvious non-diacritics
162 if( nChar < 0x0300 )
163 return false;
164 if( nChar >= 0x2100 )
165 return false;
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}
179 // TODO: almost anything is faster than an O(n) search
180 static const int nCount = SAL_N_ELEMENTS(aRanges);
181 const DiaRange* pRange = &aRanges[0];
182 for( int i = nCount; --i >= 0; ++pRange )
183 if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
184 return true;
186 return false;
189 int GetVerticalFlags( sal_UCS4 nChar )
191 if( (nChar >= 0x1100 && nChar <= 0x11f9) // Hangul Jamo
192 || (nChar == 0x2030 || nChar == 0x2031) // per mille sign
193 || (nChar >= 0x3000 && nChar <= 0xfaff) // unified CJK
194 || (nChar >= 0xfe20 && nChar <= 0xfe6f) // CJK compatibility
195 || (nChar >= 0xff00 && nChar <= 0xfffd) ) // other CJK
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
202 if((nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012)
203 || (nChar == 0xFF3B || nChar == 0xFF3D || nChar==0xFF08 || nChar==0xFF09)
204 || (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms
205 || (nChar == 0xFFE3) )
206 return GF_NONE; // not rotated
207 else if( nChar == 0x30fc )
208 return GF_ROTR; // right
209 return GF_ROTL; // left
211 else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs
212 return GF_ROTL; // left
214 return GF_NONE; // not rotated as default
217 sal_UCS4 GetMirroredChar( sal_UCS4 nChar )
219 nChar = u_charMirror( nChar );
220 return nChar;
223 sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
225 // currently only conversion from ASCII digits is interesting
226 if( (nChar < '0') || ('9' < nChar) )
227 return nChar;
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 switch( eLang & LANGUAGE_MASK_PRIMARY )
235 default:
236 nOffset = 0;
237 break;
238 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
239 nOffset = 0x0660 - '0'; // arabic-indic digits
240 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 nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
246 break;
247 case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
248 nOffset = 0x09E6 - '0'; // bengali
249 break;
250 case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
251 nOffset = 0x0966 - '0'; // devanagari
252 break;
253 case LANGUAGE_AMHARIC_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
254 case LANGUAGE_TIGRIGNA_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
255 // TODO case:
256 nOffset = 0x1369 - '0'; // ethiopic
257 break;
258 case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
259 nOffset = 0x0AE6 - '0'; // gujarati
260 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 nOffset = 0x0CE6 - '0'; // kannada
268 break;
269 case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
270 nOffset = 0x17E0 - '0'; // khmer
271 break;
272 case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
273 nOffset = 0x0ED0 - '0'; // lao
274 break;
275 case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
276 nOffset = 0x0D66 - '0'; // malayalam
277 break;
278 case LANGUAGE_MONGOLIAN_MONGOLIAN_LSO & LANGUAGE_MASK_PRIMARY:
279 switch (eLang)
281 case LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA:
282 case LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA:
283 case LANGUAGE_MONGOLIAN_MONGOLIAN_LSO:
284 nOffset = 0x1810 - '0'; // mongolian
285 break;
286 default:
287 nOffset = 0; // mongolian cyrillic
288 break;
290 break;
291 case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
292 nOffset = 0x1040 - '0'; // myanmar
293 break;
294 case LANGUAGE_ODIA & LANGUAGE_MASK_PRIMARY:
295 nOffset = 0x0B66 - '0'; // odia
296 break;
297 case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
298 nOffset = 0x0BE7 - '0'; // tamil
299 break;
300 case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
301 nOffset = 0x0C66 - '0'; // telugu
302 break;
303 case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
304 nOffset = 0x0E50 - '0'; // thai
305 break;
306 case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
307 nOffset = 0x0F20 - '0'; // tibetan
308 break;
311 nChar += nOffset;
312 return nChar;
315 inline bool IsControlChar( sal_UCS4 cChar )
317 // C0 control characters
318 if( (0x0001 <= cChar) && (cChar <= 0x001F) )
319 return true;
320 // formatting characters
321 if( (0x200E <= cChar) && (cChar <= 0x200F) )
322 return true;
323 if( (0x2028 <= cChar) && (cChar <= 0x202E) )
324 return true;
325 // deprecated formatting characters
326 if( (0x206A <= cChar) && (cChar <= 0x206F) )
327 return true;
328 if( (0x2060 == cChar) )
329 return true;
330 // byte order markers and invalid unicode
331 if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
332 return true;
333 return false;
336 bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
338 // check if charpos could extend current run
339 int nIndex = maRuns.size();
340 if( nIndex >= 2 )
342 int nRunPos0 = maRuns[ nIndex-2 ];
343 int nRunPos1 = maRuns[ nIndex-1 ];
344 if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) )
346 // extend current run by new charpos
347 maRuns[ nIndex-1 ] = nCharPos + int(!bRTL);
348 return false;
350 // ignore new charpos when it is in current run
351 if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
352 return false;
353 if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
354 return false;
357 // else append a new run consisting of the new charpos
358 maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
359 maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
360 return true;
363 bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
365 if( nCharPos0 == nCharPos1 )
366 return false;
368 // swap if needed
369 if( bRTL == (nCharPos0 < nCharPos1) )
371 int nTemp = nCharPos0;
372 nCharPos0 = nCharPos1;
373 nCharPos1 = nTemp;
376 // append new run
377 maRuns.push_back( nCharPos0 );
378 maRuns.push_back( nCharPos1 );
379 return true;
382 bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
384 if( mnRunIndex >= (int)maRuns.size() )
385 return false;
387 int nMinCharPos = maRuns[ mnRunIndex+0 ];
388 int nEndCharPos = maRuns[ mnRunIndex+1 ];
389 if( nMinCharPos > nEndCharPos ) // reversed in RTL case
391 int nTemp = nMinCharPos;
392 nMinCharPos = nEndCharPos;
393 nEndCharPos = nTemp;
396 if( nCharPos < nMinCharPos )
397 return false;
398 if( nCharPos >= nEndCharPos )
399 return false;
400 return true;
403 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
405 bool bRet = false;
406 int nRunIndex = mnRunIndex;
408 ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
410 pThis->ResetPos();
412 for (size_t i = 0; i < maRuns.size(); i+=2)
414 if( (bRet = PosIsInRun( nCharPos )) )
415 break;
416 pThis->NextRun();
419 pThis->mnRunIndex = nRunIndex;
420 return bRet;
423 bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
425 // negative nCharPos => reset to first run
426 if( *nCharPos < 0 )
427 mnRunIndex = 0;
429 // return false when all runs completed
430 if( mnRunIndex >= (int)maRuns.size() )
431 return false;
433 int nRunPos0 = maRuns[ mnRunIndex+0 ];
434 int nRunPos1 = maRuns[ mnRunIndex+1 ];
435 *bRightToLeft = (nRunPos0 > nRunPos1);
437 if( *nCharPos < 0 )
439 // get first valid nCharPos in run
440 *nCharPos = nRunPos0;
442 else
444 // advance to next nCharPos for LTR case
445 if( !*bRightToLeft )
446 ++(*nCharPos);
448 // advance to next run if current run is completed
449 if( *nCharPos == nRunPos1 )
451 if( (mnRunIndex += 2) >= (int)maRuns.size() )
452 return false;
453 nRunPos0 = maRuns[ mnRunIndex+0 ];
454 nRunPos1 = maRuns[ mnRunIndex+1 ];
455 *bRightToLeft = (nRunPos0 > nRunPos1);
456 *nCharPos = nRunPos0;
460 // advance to next nCharPos for RTL case
461 if( *bRightToLeft )
462 --(*nCharPos);
464 return true;
467 bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
469 if( mnRunIndex >= (int)maRuns.size() )
470 return false;
472 int nRunPos0 = maRuns[ mnRunIndex+0 ];
473 int nRunPos1 = maRuns[ mnRunIndex+1 ];
474 *bRightToLeft = (nRunPos1 < nRunPos0) ;
475 if( !*bRightToLeft )
477 *nMinRunPos = nRunPos0;
478 *nEndRunPos = nRunPos1;
480 else
482 *nMinRunPos = nRunPos1;
483 *nEndRunPos = nRunPos0;
485 return true;
488 ImplLayoutArgs::ImplLayoutArgs( const sal_Unicode* pStr, int nLen,
489 int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, const LanguageTag& rLanguageTag,
490 vcl::TextLayoutCache const*const pLayoutCache)
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 mnOrientation( 0 )
503 if( mnFlags & SalLayoutFlags::BiDiStrong )
505 // handle strong BiDi mode
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 bool bRTL(mnFlags & SalLayoutFlags::BiDiRtl);
511 AddRun( mnMinCharPos, mnEndCharPos, bRTL );
513 else
515 // handle weak BiDi mode
517 UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
518 if( mnFlags & SalLayoutFlags::BiDiRtl )
519 nLevel = UBIDI_DEFAULT_RTL;
521 // prepare substring for BiDi analysis
522 // TODO: reuse allocated pParaBidi
523 UErrorCode rcI18n = U_ZERO_ERROR;
524 UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
525 if( !pParaBidi )
526 return;
527 ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n ); // UChar != sal_Unicode in MinGW
529 UBiDi* pLineBidi = pParaBidi;
530 int nSubLength = mnEndCharPos - mnMinCharPos;
531 if( nSubLength != mnLength )
533 pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
534 ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
537 // run BiDi algorithm
538 const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
539 //maRuns.resize( 2 * nRunCount );
540 for( int i = 0; i < nRunCount; ++i )
542 int32_t nMinPos, nLength;
543 const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
544 const int nPos0 = nMinPos + mnMinCharPos;
545 const int nPos1 = nPos0 + nLength;
547 const bool bRTL = (nDir == UBIDI_RTL);
548 AddRun( nPos0, nPos1, bRTL );
551 // cleanup BiDi engine
552 if( pLineBidi != pParaBidi )
553 ubidi_close( pLineBidi );
554 ubidi_close( pParaBidi );
557 // prepare calls to GetNextPos/GetNextRun
558 maRuns.ResetPos();
561 // add a run after splitting it up to get rid of control chars
562 void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
564 DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
566 // remove control characters from runs by splitting them up
567 if( !bRTL )
569 for( int i = nCharPos0; i < nCharPos1; ++i )
570 if( IsControlChar( mpStr[i] ) )
572 // add run until control char
573 maRuns.AddRun( nCharPos0, i, bRTL );
574 nCharPos0 = i + 1;
577 else
579 for( int i = nCharPos1; --i >= nCharPos0; )
580 if( IsControlChar( mpStr[i] ) )
582 // add run until control char
583 maRuns.AddRun( i+1, nCharPos1, bRTL );
584 nCharPos1 = i;
588 // add remainder of run
589 maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
592 bool ImplLayoutArgs::PrepareFallback()
594 // short circuit if no fallback is needed
595 if( maFallbackRuns.IsEmpty() )
597 maRuns.Clear();
598 return false;
601 // convert the fallback requests to layout requests
602 bool bRTL;
603 int nMin, nEnd;
605 // get the individual fallback requests
606 typedef std::vector<int> IntVector;
607 IntVector aPosVector;
608 aPosVector.reserve( mnLength );
609 maFallbackRuns.ResetPos();
610 for(; maFallbackRuns.GetRun( &nMin, &nEnd, &bRTL ); maFallbackRuns.NextRun() )
611 for( int i = nMin; i < nEnd; ++i )
612 aPosVector.push_back( i );
613 maFallbackRuns.Clear();
615 // sort the individual fallback requests
616 std::sort( aPosVector.begin(), aPosVector.end() );
618 // adjust fallback runs to have the same order and limits of the original runs
619 ImplLayoutRuns aNewRuns;
620 maRuns.ResetPos();
621 for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
623 if( !bRTL) {
624 IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
625 for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
626 aNewRuns.AddPos( *it, bRTL );
627 } else {
628 IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
629 while( (it != aPosVector.begin()) && (*--it >= nMin) )
630 aNewRuns.AddPos( *it, bRTL );
634 maRuns = aNewRuns; // TODO: use vector<>::swap()
635 maRuns.ResetPos();
636 return true;
639 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
641 bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
642 maRuns.NextRun();
643 return bValid;
646 SalLayout::SalLayout()
647 : mnMinCharPos( -1 ),
648 mnEndCharPos( -1 ),
649 mnLayoutFlags( SalLayoutFlags::NONE ),
650 mnUnitsPerPixel( 1 ),
651 mnOrientation( 0 ),
652 mnRefCount( 1 ),
653 maDrawOffset( 0, 0 )
656 SalLayout::~SalLayout()
659 void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
661 mnMinCharPos = rArgs.mnMinCharPos;
662 mnEndCharPos = rArgs.mnEndCharPos;
663 mnLayoutFlags = rArgs.mnFlags;
664 mnOrientation = rArgs.mnOrientation;
667 void SalLayout::Release() const
669 // TODO: protect when multiple threads can access this
670 if( --mnRefCount > 0 )
671 return;
672 // const_cast because some compilers violate ANSI C++ spec
673 delete this;
676 Point SalLayout::GetDrawPosition( const Point& rRelative ) const
678 Point aPos = maDrawBase;
679 Point aOfs = rRelative + maDrawOffset;
681 if( mnOrientation == 0 )
682 aPos += aOfs;
683 else
685 // cache trigonometric results
686 static int nOldOrientation = 0;
687 static double fCos = 1.0, fSin = 0.0;
688 if( nOldOrientation != mnOrientation )
690 nOldOrientation = mnOrientation;
691 double fRad = mnOrientation * (M_PI / 1800.0);
692 fCos = cos( fRad );
693 fSin = sin( fRad );
696 double fX = aOfs.X();
697 double fY = aOfs.Y();
698 long nX = static_cast<long>( +fCos * fX + fSin * fY );
699 long nY = static_cast<long>( +fCos * fY - fSin * fX );
700 aPos += Point( nX, nY );
703 return aPos;
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
710 // If the range doesn't match in 0x3000 and 0x30FB, please change
711 // also ImplCalcKerning.
713 int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
715 // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
716 static const signed char nTable[0x30] =
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
723 int nResult = 0;
724 if( (c >= 0x3000) && (c < 0x3030) )
725 nResult = nTable[ c - 0x3000 ];
726 else switch( c )
728 case 0x30FB:
729 nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
730 break;
731 case 0x2019: case 0x201D:
732 case 0xFF01: case 0xFF09: case 0xFF0C:
733 case 0xFF1A: case 0xFF1B:
734 nResult = -2;
735 break;
736 case 0x2018: case 0x201C:
737 case 0xFF08:
738 nResult = +2;
739 break;
740 default:
741 break;
744 return nResult;
747 bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
748 ::basegfx::B2DPolyPolygonVector& rVector ) const
750 bool bAllOk = true;
751 bool bOneOk = false;
753 Point aPos;
754 ::basegfx::B2DPolyPolygon aGlyphOutline;
755 for( int nStart = 0;;)
757 sal_GlyphId nLGlyph;
758 if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
759 break;
761 // get outline of individual glyph, ignoring "empty" glyphs
762 bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
763 bAllOk &= bSuccess;
764 bOneOk |= bSuccess;
765 // only add non-empty outlines
766 if( bSuccess && (aGlyphOutline.count() > 0) )
768 if( aPos.X() || aPos.Y() )
770 aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
773 // insert outline at correct position
774 rVector.push_back( aGlyphOutline );
778 return (bAllOk && bOneOk);
781 bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
783 bool bRet = false;
784 rRect.SetEmpty();
786 Point aPos;
787 Rectangle aRectangle;
788 for( int nStart = 0;;)
790 sal_GlyphId nLGlyph;
791 if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
792 break;
794 // get bounding rectangle of individual glyph
795 if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
797 // merge rectangle
798 aRectangle += aPos;
799 if (rRect.IsEmpty())
800 rRect = aRectangle;
801 else
802 rRect.Union(aRectangle);
803 bRet = true;
807 return bRet;
810 bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph )
812 bool bRet = false;
813 if( nGlyph & GF_ISCHAR )
815 long nChar = nGlyph & GF_IDXMASK;
816 bRet = (nChar <= 0x0020) // blank
817 //|| (nChar == 0x00A0) // non breaking space
818 || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
819 || (nChar == 0x3000); // ideographic space
821 else
822 bRet = ((nGlyph & GF_IDXMASK) == 3);
823 return bRet;
826 GenericSalLayout::GenericSalLayout()
829 GenericSalLayout::~GenericSalLayout()
832 void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
834 m_GlyphItems.push_back(rGlyphItem);
837 bool GenericSalLayout::GetCharWidths( DeviceCoordinate* pCharWidths ) const
839 // initialize character extents buffer
840 int nCharCount = mnEndCharPos - mnMinCharPos;
841 for( int n = 0; n < nCharCount; ++n )
842 pCharWidths[n] = 0;
844 // determine cluster extents
845 for( GlyphVector::const_iterator pGlyphIter = m_GlyphItems.begin(), end = m_GlyphItems.end(); pGlyphIter != end ; ++pGlyphIter)
847 // use cluster start to get char index
848 if( !pGlyphIter->IsClusterStart() )
849 continue;
851 int n = pGlyphIter->mnCharPos;
852 if( n >= mnEndCharPos )
853 continue;
854 n -= mnMinCharPos;
855 if( n < 0 )
856 continue;
858 // left glyph in cluster defines default extent
859 long nXPosMin = pGlyphIter->maLinearPos.X();
860 long nXPosMax = nXPosMin + pGlyphIter->mnNewWidth;
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 while( (pGlyphIter+1 != end) && !pGlyphIter[1].IsClusterStart() )
867 // advance to next glyph in cluster
868 ++pGlyphIter;
870 if( pGlyphIter->IsDiacritic() )
871 continue; // ignore diacritics
872 // get leftmost x-extent of this glyph
873 long nXPos = pGlyphIter->maLinearPos.X();
874 if( nXPosMin > nXPos )
875 nXPosMin = nXPos;
877 // get rightmost x-extent of this glyph
878 nXPos += pGlyphIter->mnNewWidth;
879 if( nXPosMax < nXPos )
880 nXPosMax = nXPos;
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 for( GlyphVector::const_iterator pN = pGlyphIter; ++pN != end; )
889 if( pN->IsClusterStart() )
890 break;
891 if( pN->IsDiacritic() )
892 continue; // ignore diacritics
893 if( nXPosMax > pN->maLinearPos.X() )
894 nXPosMax = pN->maLinearPos.X();
896 if( nXPosMax < nXPosMin )
897 nXPosMin = nXPosMax = 0;
899 // character width is sum of glyph cluster widths
900 pCharWidths[n] += nXPosMax - nXPosMin;
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
908 return true;
911 DeviceCoordinate GenericSalLayout::FillDXArray( DeviceCoordinate* pCharWidths ) const
913 if( pCharWidths )
914 if( !GetCharWidths( pCharWidths ) )
915 return 0;
917 return GetTextWidth();
920 // the text width is the maximum logical extent of all glyphs
921 DeviceCoordinate GenericSalLayout::GetTextWidth() const
923 if( m_GlyphItems.empty() )
924 return 0;
926 // initialize the extent
927 DeviceCoordinate nMinPos = 0;
928 DeviceCoordinate nMaxPos = 0;
930 for( GlyphVector::const_iterator pGlyphIter = m_GlyphItems.begin(), end = m_GlyphItems.end(); pGlyphIter != end ; ++pGlyphIter )
932 // update the text extent with the glyph extent
933 DeviceCoordinate nXPos = pGlyphIter->maLinearPos.X();
934 if( nMinPos > nXPos )
935 nMinPos = nXPos;
936 nXPos += pGlyphIter->mnNewWidth - pGlyphIter->mnXOffset;
937 if( nMaxPos < nXPos )
938 nMaxPos = nXPos;
941 DeviceCoordinate nWidth = nMaxPos - nMinPos;
942 return nWidth;
945 Rectangle GenericSalLayout::GetTextRect() const
947 if( m_GlyphItems.empty() )
948 return Rectangle(Point(0, 0), Size(0, 0));
950 // initialize the extent
951 DeviceCoordinate nMinXPos = 0;
952 DeviceCoordinate nMaxXPos = 0;
954 DeviceCoordinate nMinYPos = 0;
955 DeviceCoordinate nMaxYPos = 0;
957 for( GlyphVector::const_iterator pGlyphIter = m_GlyphItems.begin(), end = m_GlyphItems.end(); pGlyphIter != end ; ++pGlyphIter )
959 // update the text extent with the glyph extent
960 DeviceCoordinate nXPos = pGlyphIter->maLinearPos.X();
961 DeviceCoordinate nYPos = pGlyphIter->maLinearPos.Y();
963 if( nMinXPos > nXPos )
964 nMinXPos = nXPos;
965 nXPos += pGlyphIter->mnNewWidth - pGlyphIter->mnXOffset;
966 if( nMaxXPos < nXPos )
967 nMaxXPos = nXPos;
969 if( nMinYPos > nYPos )
970 nMinYPos = nYPos;
971 nYPos += pGlyphIter->mnNewWidth - pGlyphIter->mnYOffset;
972 if( nMaxYPos < nYPos )
973 nMaxYPos = nYPos;
976 DeviceCoordinate nWidth = nMaxXPos - nMinXPos;
977 DeviceCoordinate nHeight = nMaxYPos - nMinYPos;
979 return Rectangle( Point(nMinXPos, nMinYPos), Size(nWidth, nHeight) );
982 void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
984 SalLayout::AdjustLayout( rArgs );
986 if( rArgs.mpDXArray )
987 ApplyDXArray( rArgs );
988 else if( rArgs.mnLayoutWidth )
989 Justify( rArgs.mnLayoutWidth );
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).
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:
999 // "An array of 32-bit unsigned integers that specify the output spacing
1000 // between the origins of adjacent character cells in logical units."
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.
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.
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.
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?
1028 void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
1030 if( m_GlyphItems.empty())
1031 return;
1033 // determine cluster boundaries and x base offset
1034 const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1035 int* pLogCluster = static_cast<int*>(alloca( nCharCount * sizeof(int) ));
1036 size_t i;
1037 int n,p;
1038 long nBasePointX = -1;
1039 if( mnLayoutFlags & SalLayoutFlags::ForFallback )
1040 nBasePointX = 0;
1041 for(p = 0; p < nCharCount; ++p )
1042 pLogCluster[ p ] = -1;
1044 for( i = 0; i < m_GlyphItems.size(); ++i)
1046 n = m_GlyphItems[i].mnCharPos - rArgs.mnMinCharPos;
1047 if( (n < 0) || (nCharCount <= n) )
1048 continue;
1049 if( pLogCluster[ n ] < 0 )
1050 pLogCluster[ n ] = i;
1051 if( nBasePointX < 0 )
1052 nBasePointX = m_GlyphItems[i].maLinearPos.X();
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 for( n = 0; n < nCharCount; ++n )
1057 if( (p = pLogCluster[n]) >= 0 )
1058 break;
1059 if( n >= nCharCount )
1060 return;
1061 for( n = 0; n < nCharCount; ++n )
1063 if( pLogCluster[ n ] < 0 )
1064 pLogCluster[ n ] = p;
1065 else
1066 p = pLogCluster[ n ];
1069 // calculate adjusted cluster widths
1070 long* pNewGlyphWidths = static_cast<long*>(alloca( m_GlyphItems.size() * sizeof(long) ));
1071 for( i = 0; i < m_GlyphItems.size(); ++i )
1072 pNewGlyphWidths[ i ] = 0;
1074 bool bRTL;
1075 for( int nCharPos = p = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
1077 n = nCharPos - rArgs.mnMinCharPos;
1078 if( (n < 0) || (nCharCount <= n) ) continue;
1080 if( pLogCluster[ n ] >= 0 )
1081 p = pLogCluster[ n ];
1082 if( p >= 0 )
1084 long nDelta = rArgs.mpDXArray[ n ] ;
1085 if( n > 0 )
1086 nDelta -= rArgs.mpDXArray[ n-1 ];
1087 pNewGlyphWidths[ p ] += nDelta * mnUnitsPerPixel;
1091 // move cluster positions using the adjusted widths
1092 long nDelta = 0;
1093 long nNewPos = 0;
1094 for( i = 0; i < m_GlyphItems.size(); ++i)
1096 if( m_GlyphItems[i].IsClusterStart() )
1098 // calculate original and adjusted cluster width
1099 int nOldClusterWidth = m_GlyphItems[i].mnNewWidth - m_GlyphItems[i].mnXOffset;
1100 int nNewClusterWidth = pNewGlyphWidths[i];
1101 size_t j;
1102 for( j = i; ++j < m_GlyphItems.size(); )
1104 if( m_GlyphItems[j].IsClusterStart() )
1105 break;
1106 if( !m_GlyphItems[j].IsDiacritic() ) // #i99367# ignore diacritics
1107 nOldClusterWidth += m_GlyphItems[j].mnNewWidth - m_GlyphItems[j].mnXOffset;
1108 nNewClusterWidth += pNewGlyphWidths[j];
1110 const int nDiff = nNewClusterWidth - nOldClusterWidth;
1112 // adjust cluster glyph widths and positions
1113 nDelta = nBasePointX + (nNewPos - m_GlyphItems[i].maLinearPos.X());
1114 if( !m_GlyphItems[i].IsRTLGlyph() )
1116 // for LTR case extend rightmost glyph in cluster
1117 m_GlyphItems[j - 1].mnNewWidth += nDiff;
1119 else
1121 // right align cluster in new space for RTL case
1122 m_GlyphItems[i].mnNewWidth += nDiff;
1123 nDelta += nDiff;
1126 nNewPos += nNewClusterWidth;
1129 m_GlyphItems[i].maLinearPos.X() += nDelta;
1133 void GenericSalLayout::Justify( DeviceCoordinate nNewWidth )
1135 nNewWidth *= mnUnitsPerPixel;
1136 DeviceCoordinate nOldWidth = GetTextWidth();
1137 if( !nOldWidth || nNewWidth==nOldWidth )
1138 return;
1140 if(m_GlyphItems.empty())
1142 return;
1144 // find rightmost glyph, it won't get stretched
1145 GlyphVector::iterator pGlyphIterRight = m_GlyphItems.begin();
1146 pGlyphIterRight += m_GlyphItems.size() - 1;
1147 GlyphVector::iterator pGlyphIter;
1148 // count stretchable glyphs
1149 int nStretchable = 0;
1150 int nMaxGlyphWidth = 0;
1151 for(pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter)
1153 if( !pGlyphIter->IsDiacritic() )
1154 ++nStretchable;
1155 if( nMaxGlyphWidth < pGlyphIter->mnOrigWidth )
1156 nMaxGlyphWidth = pGlyphIter->mnOrigWidth;
1159 // move rightmost glyph to requested position
1160 nOldWidth -= pGlyphIterRight->mnOrigWidth;
1161 if( nOldWidth <= 0 )
1162 return;
1163 if( nNewWidth < nMaxGlyphWidth)
1164 nNewWidth = nMaxGlyphWidth;
1165 nNewWidth -= pGlyphIterRight->mnOrigWidth;
1166 pGlyphIterRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
1168 // justify glyph widths and positions
1169 int nDiffWidth = nNewWidth - nOldWidth;
1170 if( nDiffWidth >= 0) // expanded case
1172 // expand width by distributing space between glyphs evenly
1173 int nDeltaSum = 0;
1174 for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
1176 // move glyph to justified position
1177 pGlyphIter->maLinearPos.X() += nDeltaSum;
1179 // do not stretch non-stretchable glyphs
1180 if( pGlyphIter->IsDiacritic() || (nStretchable <= 0) )
1181 continue;
1183 // distribute extra space equally to stretchable glyphs
1184 int nDeltaWidth = nDiffWidth / nStretchable--;
1185 nDiffWidth -= nDeltaWidth;
1186 pGlyphIter->mnNewWidth += nDeltaWidth;
1187 nDeltaSum += nDeltaWidth;
1190 else // condensed case
1192 // squeeze width by moving glyphs proportionally
1193 double fSqueeze = (double)nNewWidth / nOldWidth;
1194 if(m_GlyphItems.size() > 1)
1196 for( pGlyphIter = m_GlyphItems.begin(); ++pGlyphIter != pGlyphIterRight;)
1198 int nX = pGlyphIter->maLinearPos.X() - maBasePoint.X();
1199 nX = (int)(nX * fSqueeze);
1200 pGlyphIter->maLinearPos.X() = nX + maBasePoint.X();
1203 // adjust glyph widths to new positions
1204 for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
1205 pGlyphIter->mnNewWidth = pGlyphIter[1].maLinearPos.X() - pGlyphIter[0].maLinearPos.X();
1209 void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
1211 long nOffset = 0;
1213 for( GlyphVector::iterator pGlyphIter = m_GlyphItems.begin(), pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
1215 const int n = pGlyphIter->mnCharPos;
1216 if( n < nLength - 1)
1218 // ignore code ranges that are not affected by asian punctuation compression
1219 const sal_Unicode cHere = pStr[n];
1220 if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
1221 continue;
1222 const sal_Unicode cNext = pStr[n+1];
1223 if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
1224 continue;
1226 // calculate compression values
1227 const bool bVertical = false;
1228 long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
1229 long nKernNext = -CalcAsianKerning( cNext, false, bVertical );
1231 // apply punctuation compression to logical glyph widths
1232 long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
1233 if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
1235 int nGlyphWidth = pGlyphIter->mnOrigWidth;
1236 nDelta = (nDelta * nGlyphWidth + 2) / 4;
1237 if( pGlyphIter+1 == pGlyphIterEnd )
1238 pGlyphIter->mnNewWidth += nDelta;
1239 nOffset += nDelta;
1243 // adjust the glyph positions to the new glyph widths
1244 if( pGlyphIter+1 != pGlyphIterEnd )
1245 pGlyphIter->maLinearPos.X() += nOffset;
1249 void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
1251 // TODO: reimplement method when container type for GlyphItems changes
1253 // skip if the kashida glyph in the font looks suspicious
1254 if( nKashidaWidth <= 0 )
1255 return;
1257 // calculate max number of needed kashidas
1258 int nKashidaCount = 0;
1259 for (GlyphVector::iterator pGlyphIter = m_GlyphItems.begin();
1260 pGlyphIter != m_GlyphItems.end(); ++pGlyphIter)
1262 // only inject kashidas in RTL contexts
1263 if( !pGlyphIter->IsRTLGlyph() )
1264 continue;
1265 // no kashida-injection for blank justified expansion either
1266 if( IsSpacingGlyph( pGlyphIter->maGlyphId) )
1267 continue;
1269 // calculate gap, ignore if too small
1270 int nGapWidth = pGlyphIter->mnNewWidth - pGlyphIter->mnOrigWidth;
1271 // worst case is one kashida even for mini-gaps
1272 if( nGapWidth < nKashidaWidth )
1273 continue;
1275 nKashidaCount = 0;
1276 Point aPos = pGlyphIter->maLinearPos;
1277 aPos.X() -= nGapWidth; // cluster is already right aligned
1278 int const nCharPos = pGlyphIter->mnCharPos;
1279 GlyphVector::iterator pGlyphIter2 = pGlyphIter;
1280 for(; nGapWidth > nKashidaWidth; nGapWidth -= nKashidaWidth, ++nKashidaCount )
1282 pGlyphIter2 = m_GlyphItems.insert(pGlyphIter2, GlyphItem(nCharPos, nKashidaIndex, aPos,
1283 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth ));
1284 ++pGlyphIter2;
1285 aPos.X() += nKashidaWidth;
1288 // fixup rightmost kashida for gap remainder
1289 if( nGapWidth > 0 )
1291 pGlyphIter2 = m_GlyphItems.insert(pGlyphIter2, GlyphItem(nCharPos, nKashidaIndex, aPos,
1292 GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaCount ? nGapWidth : nGapWidth/2 ));
1293 ++pGlyphIter2;
1294 aPos.X() += nGapWidth;
1296 pGlyphIter = pGlyphIter2;
1300 void GenericSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
1302 // initialize result array
1303 for (int i = 0; i < nMaxIndex; ++i)
1304 pCaretXArray[i] = -1;
1306 // calculate caret positions using glyph array
1307 for( GlyphVector::const_iterator pGlyphIter = m_GlyphItems.begin(), pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
1309 long nXPos = pGlyphIter->maLinearPos.X();
1310 long nXRight = nXPos + pGlyphIter->mnOrigWidth;
1311 int n = pGlyphIter->mnCharPos;
1312 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 if (!pGlyphIter->IsClusterStart() && pCaretXArray[nCurrIdx] != -1)
1315 continue;
1316 if( !pGlyphIter->IsRTLGlyph() )
1318 // normal positions for LTR case
1319 pCaretXArray[ nCurrIdx ] = nXPos;
1320 pCaretXArray[ nCurrIdx+1 ] = nXRight;
1322 else
1324 // reverse positions for RTL case
1325 pCaretXArray[ nCurrIdx ] = nXRight;
1326 pCaretXArray[ nCurrIdx+1 ] = nXPos;
1331 sal_Int32 GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1333 int nCharCapacity = mnEndCharPos - mnMinCharPos;
1334 DeviceCoordinate* pCharWidths = static_cast<DeviceCoordinate*>(alloca( nCharCapacity * sizeof(DeviceCoordinate) ));
1335 if( !GetCharWidths( pCharWidths ) )
1336 return -1;
1338 DeviceCoordinate nWidth = 0;
1339 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1341 nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
1342 if( nWidth > nMaxWidth )
1343 return i;
1344 nWidth += nCharExtra;
1347 return -1;
1350 int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1351 int& nStart, DeviceCoordinate* pGlyphAdvAry, int* pCharPosAry,
1352 const PhysicalFontFace** /*pFallbackFonts*/ ) const
1354 GlyphVector::const_iterator pGlyphIter = m_GlyphItems.begin();
1355 GlyphVector::const_iterator pGlyphIterEnd = m_GlyphItems.end();
1356 pGlyphIter += nStart;
1358 // find next glyph in substring
1359 for(; pGlyphIter != pGlyphIterEnd; ++nStart, ++pGlyphIter )
1361 int n = pGlyphIter->mnCharPos;
1362 if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
1363 break;
1366 // return zero if no more glyph found
1367 if( nStart >= (int)m_GlyphItems.size() )
1368 return 0;
1370 if( pGlyphIter == pGlyphIterEnd )
1371 return 0;
1373 // calculate absolute position in pixel units
1374 Point aRelativePos = pGlyphIter->maLinearPos - maBasePoint;
1376 // find more glyphs which can be merged into one drawing instruction
1377 int nCount = 0;
1378 long nYPos = pGlyphIter->maLinearPos.Y();
1379 long nOldFlags = pGlyphIter->maGlyphId;
1380 for(;;)
1382 // update return data with glyph info
1383 ++nCount;
1384 *(pGlyphs++) = pGlyphIter->maGlyphId;
1385 if( pCharPosAry )
1386 *(pCharPosAry++) = pGlyphIter->mnCharPos;
1387 if( pGlyphAdvAry )
1388 *pGlyphAdvAry = pGlyphIter->mnNewWidth;
1390 // break at end of glyph list
1391 if( ++nStart >= (int)m_GlyphItems.size() )
1392 break;
1393 // break when enough glyphs
1394 if( nCount >= nLen )
1395 break;
1397 long nGlyphAdvance = pGlyphIter[1].maLinearPos.X() - pGlyphIter->maLinearPos.X();
1398 if( pGlyphAdvAry )
1400 // override default advance width with correct value
1401 *(pGlyphAdvAry++) = nGlyphAdvance;
1403 else
1405 // stop when next x-position is unexpected
1406 if( pGlyphIter->mnOrigWidth != nGlyphAdvance )
1407 break;
1410 // advance to next glyph
1411 ++pGlyphIter;
1413 // stop when next y-position is unexpected
1414 if( nYPos != pGlyphIter->maLinearPos.Y() )
1415 break;
1417 // stop when no longer in string
1418 int n = pGlyphIter->mnCharPos;
1419 if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
1420 break;
1422 // stop when glyph flags change
1423 if( (nOldFlags ^ pGlyphIter->maGlyphId) & GF_FLAGMASK )
1424 break;
1426 nOldFlags = pGlyphIter->maGlyphId; // &GF_FLAGMASK not needed for test above
1429 aRelativePos.X() /= mnUnitsPerPixel;
1430 aRelativePos.Y() /= mnUnitsPerPixel;
1431 rPos = GetDrawPosition( aRelativePos );
1433 return nCount;
1436 void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
1438 if( nStart >= (int)m_GlyphItems.size() )
1439 return;
1441 GlyphVector::iterator pGlyphIter = m_GlyphItems.begin();
1442 pGlyphIter += nStart;
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 if( pGlyphIter->IsRTLGlyph() )
1448 nNewXPos += pGlyphIter->mnNewWidth - pGlyphIter->mnOrigWidth;
1449 // calculate the x-offset to the old position
1450 long nXDelta = nNewXPos - pGlyphIter->maLinearPos.X();
1451 // adjust all following glyph positions if needed
1452 if( nXDelta != 0 )
1454 for( GlyphVector::iterator pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
1456 pGlyphIter->maLinearPos.X() += nXDelta;
1461 void GenericSalLayout::DropGlyph( int nStart )
1463 if( nStart >= (int)m_GlyphItems.size())
1464 return;
1466 GlyphVector::iterator pGlyphIter = m_GlyphItems.begin();
1467 pGlyphIter += nStart;
1468 pGlyphIter->maGlyphId = GF_DROPPED;
1469 pGlyphIter->mnCharPos = -1;
1472 void GenericSalLayout::Simplify( bool bIsBase )
1474 const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
1476 // remove dropped glyphs inplace
1477 size_t j = 0;
1478 for(size_t i = 0; i < m_GlyphItems.size(); i++ )
1480 if( m_GlyphItems[i].maGlyphId == nDropMarker )
1481 continue;
1483 if( i != j )
1485 m_GlyphItems[j] = m_GlyphItems[i];
1487 j += 1;
1489 m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
1492 // make sure GlyphItems are sorted left to right
1493 void GenericSalLayout::SortGlyphItems()
1495 // move cluster components behind their cluster start (especially for RTL)
1496 // using insertion sort because the glyph items are "almost sorted"
1498 for( GlyphVector::iterator pGlyphIter = m_GlyphItems.begin(), pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
1500 // find a cluster starting with a diacritic
1501 if( !pGlyphIter->IsDiacritic() )
1502 continue;
1503 if( !pGlyphIter->IsClusterStart() )
1504 continue;
1505 for( GlyphVector::iterator pBaseGlyph = pGlyphIter; ++pBaseGlyph != pGlyphIterEnd; )
1507 // find the base glyph matching to the misplaced diacritic
1508 if( pBaseGlyph->IsClusterStart() )
1509 break;
1510 if( pBaseGlyph->IsDiacritic() )
1511 continue;
1513 // found the matching base glyph
1514 // => this base glyph becomes the new cluster start
1515 iter_swap(pGlyphIter, pBaseGlyph);
1517 // update glyph flags of swapped glyphitems
1518 pGlyphIter->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
1519 pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
1520 // prepare for checking next cluster
1521 pGlyphIter = pBaseGlyph;
1522 break;
1527 MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const PhysicalFontFace* pBaseFont )
1528 : SalLayout()
1529 , mnLevel( 1 )
1530 , mbInComplete( false )
1532 //maFallbackRuns[0].Clear();
1533 mpFallbackFonts[ 0 ] = pBaseFont;
1534 mpLayouts[ 0 ] = &rBaseLayout;
1535 mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
1538 void MultiSalLayout::SetInComplete(bool bInComplete)
1540 mbInComplete = bInComplete;
1541 maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
1544 MultiSalLayout::~MultiSalLayout()
1546 for( int i = 0; i < mnLevel; ++i )
1547 mpLayouts[ i ]->Release();
1550 bool MultiSalLayout::AddFallback( SalLayout& rFallback,
1551 ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
1553 if( mnLevel >= MAX_FALLBACK )
1554 return false;
1556 mpFallbackFonts[ mnLevel ] = pFallbackFont;
1557 mpLayouts[ mnLevel ] = &rFallback;
1558 maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1559 ++mnLevel;
1560 return true;
1563 bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
1565 if( mnLevel <= 1 )
1566 return false;
1567 if (!mbInComplete)
1568 maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1569 return true;
1572 void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1574 SalLayout::AdjustLayout( rArgs );
1575 ImplLayoutArgs aMultiArgs = rArgs;
1576 boost::scoped_array<DeviceCoordinate> pJustificationArray;
1578 if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1580 // for stretched text in a MultiSalLayout the target width needs to be
1581 // distributed by individually adjusting its virtual character widths
1582 DeviceCoordinate nTargetWidth = aMultiArgs.mnLayoutWidth;
1583 nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1584 aMultiArgs.mnLayoutWidth = 0;
1586 // we need to get the original unmodified layouts ready
1587 for( int n = 0; n < mnLevel; ++n )
1588 mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1589 // then we can measure the unmodified metrics
1590 int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1591 pJustificationArray.reset(new DeviceCoordinate[nCharCount]);
1592 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 DeviceCoordinate nOrigWidth = 0;
1597 int nStretchable = 0;
1598 for( int i = 0; i < nCharCount; ++i )
1600 // convert array from widths to sum of widths
1601 nOrigWidth += pJustificationArray[i];
1602 if( pJustificationArray[i] > 0 )
1603 ++nStretchable;
1606 // now we are able to distribute the extra width over the virtual char widths
1607 if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1609 DeviceCoordinate nDiffWidth = nTargetWidth - nOrigWidth;
1610 DeviceCoordinate nWidthSum = 0;
1611 for( int i = 0; i < nCharCount; ++i )
1613 DeviceCoordinate nJustWidth = pJustificationArray[i];
1614 if( (nJustWidth > 0) && (nStretchable > 0) )
1616 DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
1617 nJustWidth += nDeltaWidth;
1618 nDiffWidth -= nDeltaWidth;
1619 --nStretchable;
1621 nWidthSum += nJustWidth;
1622 pJustificationArray[i] = nWidthSum;
1624 if( nWidthSum != nTargetWidth )
1625 pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1627 // the justification array is still in base level units
1628 // => convert it to pixel units
1629 if( mnUnitsPerPixel > 1 )
1631 for( int i = 0; i < nCharCount; ++i )
1633 DeviceCoordinate nVal = pJustificationArray[ i ];
1634 nVal += (mnUnitsPerPixel + 1) / 2;
1635 pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1639 // change the mpDXArray temporarily (just for the justification)
1640 aMultiArgs.mpDXArray = pJustificationArray.get();
1644 // Compute rtl flags, since in some scripts glyphs/char order can be
1645 // reversed for a few character sequencies e.g. Myanmar
1646 std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1647 rArgs.ResetPos();
1648 bool bRtl;
1649 int nRunStart, nRunEnd;
1650 while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1652 if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1653 vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1655 rArgs.ResetPos();
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 int nValid[ MAX_FALLBACK ] = {0};
1664 sal_GlyphId nDummy;
1665 Point aPos;
1666 int nLevel = 0, n;
1667 for( n = 0; n < mnLevel; ++n )
1669 // now adjust the individual components
1670 if( n > 0 )
1672 aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1673 aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback;
1675 mpLayouts[n]->AdjustLayout( aMultiArgs );
1677 // disable glyph-injection for glyph-fallback SalLayout iteration
1678 SalLayout::DisableGlyphInjection( true );
1680 // remove unused parts of component
1681 if( n > 0 )
1683 if (mbInComplete && (n == mnLevel-1))
1684 mpLayouts[n]->Simplify( true );
1685 else
1686 mpLayouts[n]->Simplify( false );
1689 // prepare merging components
1690 nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1691 nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1692 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 if( (n > 0) && !nValid[ nLevel ] )
1699 // an empty fallback layout can be released
1700 mpLayouts[n]->Release();
1702 else
1704 // reshuffle used fallbacks if needed
1705 if( nLevel != n )
1707 mpLayouts[ nLevel ] = mpLayouts[ n ];
1708 mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ];
1709 maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1711 ++nLevel;
1714 mnLevel = nLevel;
1716 // prepare merge the fallback levels
1717 long nXPos = 0;
1718 double fUnitMul = 1.0;
1719 for( n = 0; n < nLevel; ++n )
1720 maFallbackRuns[n].ResetPos();
1721 // get the next codepoint index that needs fallback
1722 int nActiveCharPos = nCharPos[0];
1723 int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1724 // get the end index of the active run
1725 int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
1726 rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1727 int nRunVisibleEndChar = nCharPos[0];
1728 // merge the fallback levels
1729 while( nValid[0] && (nLevel > 0))
1731 // find best fallback level
1732 for( n = 0; n < nLevel; ++n )
1733 if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1734 // fallback level n wins when it requested no further fallback
1735 break;
1736 int nFBLevel = n;
1738 if( n < nLevel )
1740 // use base(n==0) or fallback(n>=1) level
1741 fUnitMul = mnUnitsPerPixel;
1742 fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1743 long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1744 mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1746 else
1748 n = 0; // keep NotDef in base level
1749 fUnitMul = 1.0;
1752 if( n > 0 )
1754 // drop the NotDef glyphs in the base layout run if a fallback run exists
1755 while (
1756 (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
1757 (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
1760 mpLayouts[0]->DropGlyph( nStartOld[0] );
1761 nStartOld[0] = nStartNew[0];
1762 nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
1763 nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
1764 #ifdef MULTI_SL_DEBUG
1765 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]]);
1766 #endif
1767 if( !nValid[0] )
1768 break;
1772 // skip to end of layout run and calculate its advance width
1773 DeviceCoordinate nRunAdvance = 0;
1774 bool bKeepNotDef = (nFBLevel >= nLevel);
1775 for(;;)
1777 nRunAdvance += nGlyphAdv[n];
1779 // proceed to next glyph
1780 nStartOld[n] = nStartNew[n];
1781 int nOrigCharPos = nCharPos[n];
1782 nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1783 nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
1784 #ifdef MULTI_SL_DEBUG
1785 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]]);
1786 #endif
1787 // break after last glyph of active layout
1788 if( !nValid[n] )
1790 // performance optimization (when a fallback layout is no longer needed)
1791 if( n >= nLevel-1 )
1792 --nLevel;
1793 break;
1796 //If the next character is one which belongs to the next level, then we
1797 //are finished here for now, and we'll pick up after the next level has
1798 //been processed
1799 if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
1801 if (nOrigCharPos < nCharPos[n])
1803 if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
1804 break;
1806 else if (nOrigCharPos > nCharPos[n])
1808 if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
1809 break;
1813 // break at end of layout run
1814 if( n > 0 )
1816 // skip until end of fallback run
1817 if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
1818 break;
1820 else
1822 // break when a fallback is needed and available
1823 bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
1824 if( bNeedFallback )
1825 if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
1826 break;
1827 // break when change from resolved to unresolved base layout run
1828 if( bKeepNotDef && !bNeedFallback )
1829 { maFallbackRuns[0].NextRun(); break; }
1830 bKeepNotDef = bNeedFallback;
1832 // check for reordered glyphs
1833 if (aMultiArgs.mpDXArray &&
1834 nRunVisibleEndChar < mnEndCharPos &&
1835 nRunVisibleEndChar >= mnMinCharPos &&
1836 nCharPos[n] < mnEndCharPos &&
1837 nCharPos[n] >= mnMinCharPos)
1839 if (vRtl[nActiveCharPos - mnMinCharPos])
1841 if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1842 >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1844 nRunVisibleEndChar = nCharPos[n];
1847 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1848 <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1850 nRunVisibleEndChar = nCharPos[n];
1855 // if a justification array is available
1856 // => use it directly to calculate the corresponding run width
1857 if( aMultiArgs.mpDXArray )
1859 // the run advance is the width from the first char
1860 // in the run to the first char in the next run
1861 nRunAdvance = 0;
1862 #ifdef MULTI_SL_DEBUG
1863 const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
1864 int nOldRunAdv = 0;
1865 int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
1866 if( nDXIndex >= 0 )
1867 nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
1868 nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
1869 if( nDXIndex >= 0 )
1870 nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
1871 if( !bLTR )
1872 nOldRunAdv = -nOldRunAdv;
1873 #endif
1874 nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1875 if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
1877 if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1878 nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1879 if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1880 nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
1881 #ifdef MULTI_SL_DEBUG
1882 fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
1883 #endif
1885 else
1887 if (nRunVisibleEndChar >= mnMinCharPos)
1888 nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1889 if (nLastRunEndChar >= mnMinCharPos)
1890 nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
1891 #ifdef MULTI_SL_DEBUG
1892 fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
1893 #endif
1895 nLastRunEndChar = nRunVisibleEndChar;
1896 nRunVisibleEndChar = nCharPos[0];
1897 // the requested width is still in pixel units
1898 // => convert it to base level font units
1899 nRunAdvance *= mnUnitsPerPixel;
1901 else
1903 // the measured width is still in fallback font units
1904 // => convert it to base level font units
1905 if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1906 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
1909 // calculate new x position (in base level units)
1910 nXPos += nRunAdvance;
1912 // prepare for next fallback run
1913 nActiveCharPos = nCharPos[0];
1914 // it essential that the runs don't get ahead of themselves and in the
1915 // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1916 // have already been reached on the base level
1917 for( int i = nFBLevel; --i >= 0;)
1919 if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1921 if (bRtl)
1923 if (nRunStart > nActiveCharPos)
1924 maFallbackRuns[i].NextRun();
1926 else
1928 if (nRunEnd <= nActiveCharPos)
1929 maFallbackRuns[i].NextRun();
1935 mpLayouts[0]->Simplify( true );
1937 // reenable glyph-injection
1938 for( n = 0; n < mnLevel; ++n )
1939 SalLayout::DisableGlyphInjection( false );
1942 void MultiSalLayout::InitFont() const
1944 if( mnLevel > 0 )
1945 mpLayouts[0]->InitFont();
1948 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1950 for( int i = mnLevel; --i >= 0; )
1952 SalLayout& rLayout = *mpLayouts[ i ];
1953 rLayout.DrawBase() += maDrawBase;
1954 rLayout.DrawOffset() += maDrawOffset;
1955 rLayout.InitFont();
1956 rLayout.DrawText( rGraphics );
1957 rLayout.DrawOffset() -= maDrawOffset;
1958 rLayout.DrawBase() -= maDrawBase;
1960 // NOTE: now the baselevel font is active again
1963 sal_Int32 MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1965 if( mnLevel <= 0 )
1966 return -1;
1967 if( mnLevel == 1 )
1968 return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1970 int nCharCount = mnEndCharPos - mnMinCharPos;
1971 DeviceCoordinate* pCharWidths = static_cast<DeviceCoordinate*>(alloca( 2*nCharCount * sizeof(DeviceCoordinate) ));
1972 mpLayouts[0]->FillDXArray( pCharWidths );
1974 for( int n = 1; n < mnLevel; ++n )
1976 SalLayout& rLayout = *mpLayouts[ n ];
1977 rLayout.FillDXArray( pCharWidths + nCharCount );
1978 double fUnitMul = mnUnitsPerPixel;
1979 fUnitMul /= rLayout.GetUnitsPerPixel();
1980 for( int i = 0; i < nCharCount; ++i )
1982 if( pCharWidths[ i ] == 0 )
1984 DeviceCoordinate w = pCharWidths[ i + nCharCount ];
1985 w = (DeviceCoordinate)(w * fUnitMul + 0.5);
1986 pCharWidths[ i ] = w;
1991 DeviceCoordinate nWidth = 0;
1992 for( int i = 0; i < nCharCount; ++i )
1994 nWidth += pCharWidths[ i ] * nFactor;
1995 if( nWidth > nMaxWidth )
1996 return (i + mnMinCharPos);
1997 nWidth += nCharExtra;
2000 return -1;
2003 DeviceCoordinate MultiSalLayout::FillDXArray( DeviceCoordinate* pCharWidths ) const
2005 DeviceCoordinate nMaxWidth = 0;
2007 // prepare merging of fallback levels
2008 DeviceCoordinate* pTempWidths = NULL;
2009 const int nCharCount = mnEndCharPos - mnMinCharPos;
2010 if( pCharWidths )
2012 for( int i = 0; i < nCharCount; ++i )
2013 pCharWidths[i] = 0;
2014 pTempWidths = static_cast<DeviceCoordinate*>(alloca( nCharCount * sizeof(DeviceCoordinate) ));
2017 for( int n = mnLevel; --n >= 0; )
2019 // query every fallback level
2020 DeviceCoordinate nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
2021 if( !nTextWidth )
2022 continue;
2023 // merge results from current level
2024 double fUnitMul = mnUnitsPerPixel;
2025 fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2026 nTextWidth = (DeviceCoordinate)(nTextWidth * fUnitMul + 0.5);
2027 if( nMaxWidth < nTextWidth )
2028 nMaxWidth = nTextWidth;
2029 if( !pCharWidths )
2030 continue;
2031 // calculate virtual char widths using most probable fallback layout
2032 for( int i = 0; i < nCharCount; ++i )
2034 // #i17359# restriction:
2035 // one char cannot be resolved from different fallbacks
2036 if( pCharWidths[i] != 0 )
2037 continue;
2038 DeviceCoordinate nCharWidth = pTempWidths[i];
2039 if( !nCharWidth )
2040 continue;
2041 nCharWidth = (DeviceCoordinate)(nCharWidth * fUnitMul + 0.5);
2042 pCharWidths[i] = nCharWidth;
2046 return nMaxWidth;
2049 void MultiSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
2051 SalLayout& rLayout = *mpLayouts[ 0 ];
2052 rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
2054 if( mnLevel > 1 )
2056 long* pTempPos = static_cast<long*>(alloca( nMaxIndex * sizeof(long) ));
2057 for( int n = 1; n < mnLevel; ++n )
2059 mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
2060 double fUnitMul = mnUnitsPerPixel;
2061 fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2062 for( int i = 0; i < nMaxIndex; ++i )
2063 if( pTempPos[i] >= 0 )
2065 long w = pTempPos[i];
2066 w = static_cast<long>(w*fUnitMul + 0.5);
2067 pCaretXArray[i] = w;
2073 int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
2074 int& nStart, DeviceCoordinate* pGlyphAdvAry, int* pCharPosAry,
2075 const PhysicalFontFace** pFallbackFonts ) const
2077 // for multi-level fallback only single glyphs should be used
2078 if( mnLevel > 1 && nLen > 1 )
2079 nLen = 1;
2081 // NOTE: nStart is tagged with current font index
2082 int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
2083 nStart &= ~GF_FONTMASK;
2084 for(; nLevel < mnLevel; ++nLevel, nStart=0 )
2086 SalLayout& rLayout = *mpLayouts[ nLevel ];
2087 rLayout.InitFont();
2088 int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
2089 nStart, pGlyphAdvAry, pCharPosAry );
2090 if( nRetVal )
2092 int nFontTag = nLevel << GF_FONTSHIFT;
2093 nStart |= nFontTag;
2094 double fUnitMul = mnUnitsPerPixel;
2095 fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
2096 for( int i = 0; i < nRetVal; ++i )
2098 if( pGlyphAdvAry )
2100 DeviceCoordinate w = pGlyphAdvAry[i];
2101 w = static_cast<DeviceCoordinate>(w * fUnitMul + 0.5);
2102 pGlyphAdvAry[i] = w;
2104 pGlyphIdxAry[ i ] |= nFontTag;
2105 if( pFallbackFonts )
2107 pFallbackFonts[ i ] = mpFallbackFonts[ nLevel ];
2110 rPos += maDrawBase;
2111 rPos += maDrawOffset;
2112 return nRetVal;
2116 // #111016# reset to base level font when done
2117 mpLayouts[0]->InitFont();
2118 return 0;
2121 bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
2122 ::basegfx::B2DPolyPolygonVector& rPPV ) const
2124 bool bRet = false;
2126 for( int i = mnLevel; --i >= 0; )
2128 SalLayout& rLayout = *mpLayouts[ i ];
2129 rLayout.DrawBase() = maDrawBase;
2130 rLayout.DrawOffset() += maDrawOffset;
2131 rLayout.InitFont();
2132 bRet |= rLayout.GetOutline( rGraphics, rPPV );
2133 rLayout.DrawOffset() -= maDrawOffset;
2136 return bRet;
2139 std::shared_ptr<vcl::TextLayoutCache> SalLayout::CreateTextLayoutCache(
2140 OUString const&) const
2142 return 0; // by default, nothing to cache
2145 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */