Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / sallayout.cxx
blob30f2e60292f00e488b283144e591e2755bbf916f
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>
29 #include <salgdi.hxx>
30 #include <sallayout.hxx>
31 #include <basegfx/polygon/b2dpolypolygon.hxx>
32 #include <basegfx/matrix/b2dhommatrix.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 #include <i18nlangtag/lang.h>
37 #include <vcl/svapp.hxx>
39 #include <limits.h>
41 #include <unicode/ubidi.h>
42 #include <unicode/uchar.h>
44 #include <algorithm>
45 #include <memory>
47 // Glyph Flags
48 #define GF_FONTMASK 0xF0000000
49 #define GF_FONTSHIFT 28
50 #define GF_DROPPED 0xFFFFFFFF
53 std::ostream &operator <<(std::ostream& s, ImplLayoutArgs &rArgs)
55 #ifndef SAL_LOG_INFO
56 (void) rArgs;
57 #else
58 s << "ImplLayoutArgs{";
60 s << "Flags=";
61 if (rArgs.mnFlags == SalLayoutFlags::NONE)
62 s << 0;
63 else {
64 bool need_or = false;
65 s << "{";
66 #define TEST(x) if (rArgs.mnFlags & SalLayoutFlags::x) { if (need_or) s << "|"; s << #x; need_or = true; }
67 TEST(BiDiRtl);
68 TEST(BiDiStrong);
69 TEST(RightAlign);
70 TEST(DisableKerning);
71 TEST(KerningAsian);
72 TEST(Vertical);
73 TEST(EnableLigatures);
74 TEST(SubstituteDigits);
75 TEST(KashidaJustification);
76 TEST(ForFallback);
77 #undef TEST
78 s << "}";
81 const int nLength = rArgs.mrStr.getLength();
83 s << ",Length=" << nLength;
84 s << ",MinCharPos=" << rArgs.mnMinCharPos;
85 s << ",EndCharPos=" << rArgs.mnEndCharPos;
87 s << ",Str=\"";
88 int lim = nLength;
89 if (lim > 10)
90 lim = 7;
91 for (int i = 0; i < lim; i++) {
92 if (rArgs.mrStr[i] == '\n')
93 s << "\\n";
94 else if (rArgs.mrStr[i] < ' ' || (rArgs.mrStr[i] >= 0x7F && rArgs.mrStr[i] <= 0xFF))
95 s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << (int) rArgs.mrStr[i] << std::setfill(' ') << std::setw(1) << std::dec;
96 else if (rArgs.mrStr[i] < 0x7F)
97 s << (char) rArgs.mrStr[i];
98 else
99 s << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int) rArgs.mrStr[i] << std::setfill(' ') << std::setw(1) << std::dec;
101 if (nLength > lim)
102 s << "...";
103 s << "\"";
105 s << ",DXArray=";
106 if (rArgs.mpDXArray) {
107 s << "[";
108 int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
109 lim = count;
110 if (lim > 10)
111 lim = 7;
112 for (int i = 0; i < lim; i++) {
113 s << rArgs.mpDXArray[i];
114 if (i < lim-1)
115 s << ",";
117 if (count > lim) {
118 if (count > lim + 1)
119 s << "...";
120 s << rArgs.mpDXArray[count-1];
122 s << "]";
123 } else
124 s << "NULL";
126 s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
128 s << "}";
130 #endif
131 return s;
134 sal_UCS4 GetMirroredChar( sal_UCS4 nChar )
136 nChar = u_charMirror( nChar );
137 return nChar;
140 sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
142 // currently only conversion from ASCII digits is interesting
143 if( (nChar < '0') || ('9' < nChar) )
144 return nChar;
146 int nOffset;
147 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
148 // CAVEAT! To some like Mongolian MS assigned the same primary language
149 // although the script type is different!
150 LanguageType pri = primary(eLang);
151 if( pri == primary(LANGUAGE_ARABIC_SAUDI_ARABIA) )
152 nOffset = 0x0660 - '0'; // arabic-indic digits
153 else if ( pri.anyOf(
154 primary(LANGUAGE_FARSI),
155 primary(LANGUAGE_URDU_PAKISTAN),
156 primary(LANGUAGE_PUNJABI), //???
157 primary(LANGUAGE_SINDHI)))
158 nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
159 else if ( pri == primary(LANGUAGE_BENGALI) )
160 nOffset = 0x09E6 - '0'; // bengali
161 else if ( pri == primary(LANGUAGE_HINDI) )
162 nOffset = 0x0966 - '0'; // devanagari
163 else if ( pri.anyOf(
164 primary(LANGUAGE_AMHARIC_ETHIOPIA),
165 primary(LANGUAGE_TIGRIGNA_ETHIOPIA)))
166 // TODO case:
167 nOffset = 0x1369 - '0'; // ethiopic
168 else if ( pri == primary(LANGUAGE_GUJARATI) )
169 nOffset = 0x0AE6 - '0'; // gujarati
170 #ifdef LANGUAGE_GURMUKHI // TODO case:
171 else if ( pri == primary(LANGUAGE_GURMUKHI) )
172 nOffset = 0x0A66 - '0'; // gurmukhi
173 #endif
174 else if ( pri == primary(LANGUAGE_KANNADA) )
175 nOffset = 0x0CE6 - '0'; // kannada
176 else if ( pri == primary(LANGUAGE_KHMER))
177 nOffset = 0x17E0 - '0'; // khmer
178 else if ( pri == primary(LANGUAGE_LAO) )
179 nOffset = 0x0ED0 - '0'; // lao
180 else if ( pri == primary(LANGUAGE_MALAYALAM) )
181 nOffset = 0x0D66 - '0'; // malayalam
182 else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_LSO))
184 if (eLang.anyOf(
185 LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA,
186 LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA,
187 LANGUAGE_MONGOLIAN_MONGOLIAN_LSO))
188 nOffset = 0x1810 - '0'; // mongolian
189 else
190 nOffset = 0; // mongolian cyrillic
192 else if ( pri == primary(LANGUAGE_BURMESE) )
193 nOffset = 0x1040 - '0'; // myanmar
194 else if ( pri == primary(LANGUAGE_ODIA) )
195 nOffset = 0x0B66 - '0'; // odia
196 else if ( pri == primary(LANGUAGE_TAMIL) )
197 nOffset = 0x0BE7 - '0'; // tamil
198 else if ( pri == primary(LANGUAGE_TELUGU) )
199 nOffset = 0x0C66 - '0'; // telugu
200 else if ( pri == primary(LANGUAGE_THAI) )
201 nOffset = 0x0E50 - '0'; // thai
202 else if ( pri == primary(LANGUAGE_TIBETAN) )
203 nOffset = 0x0F20 - '0'; // tibetan
204 else
206 nOffset = 0;
209 nChar += nOffset;
210 return nChar;
213 inline bool IsControlChar( sal_UCS4 cChar )
215 // C0 control characters
216 if( (0x0001 <= cChar) && (cChar <= 0x001F) )
217 return true;
218 // formatting characters
219 if( (0x200E <= cChar) && (cChar <= 0x200F) )
220 return true;
221 if( (0x2028 <= cChar) && (cChar <= 0x202E) )
222 return true;
223 // deprecated formatting characters
224 if( (0x206A <= cChar) && (cChar <= 0x206F) )
225 return true;
226 if( (0x2060 == cChar) )
227 return true;
228 // byte order markers and invalid unicode
229 if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
230 return true;
231 return false;
234 void ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
236 // check if charpos could extend current run
237 int nIndex = maRuns.size();
238 if( nIndex >= 2 )
240 int nRunPos0 = maRuns[ nIndex-2 ];
241 int nRunPos1 = maRuns[ nIndex-1 ];
242 if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) )
244 // extend current run by new charpos
245 maRuns[ nIndex-1 ] = nCharPos + int(!bRTL);
246 return;
248 // ignore new charpos when it is in current run
249 if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
250 return;
251 if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
252 return;
255 // else append a new run consisting of the new charpos
256 maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
257 maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
260 void ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
262 if( nCharPos0 == nCharPos1 )
263 return;
265 // swap if needed
266 if( bRTL == (nCharPos0 < nCharPos1) )
268 int nTemp = nCharPos0;
269 nCharPos0 = nCharPos1;
270 nCharPos1 = nTemp;
273 // append new run
274 maRuns.push_back( nCharPos0 );
275 maRuns.push_back( nCharPos1 );
278 bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
280 if( mnRunIndex >= (int)maRuns.size() )
281 return false;
283 int nMinCharPos = maRuns[ mnRunIndex+0 ];
284 int nEndCharPos = maRuns[ mnRunIndex+1 ];
285 if( nMinCharPos > nEndCharPos ) // reversed in RTL case
287 int nTemp = nMinCharPos;
288 nMinCharPos = nEndCharPos;
289 nEndCharPos = nTemp;
292 if( nCharPos < nMinCharPos )
293 return false;
294 if( nCharPos >= nEndCharPos )
295 return false;
296 return true;
299 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
301 bool bRet = false;
302 int nRunIndex = mnRunIndex;
304 ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
306 pThis->ResetPos();
308 for (size_t i = 0; i < maRuns.size(); i+=2)
310 bRet = PosIsInRun( nCharPos );
311 if( bRet )
312 break;
313 pThis->NextRun();
316 pThis->mnRunIndex = nRunIndex;
317 return bRet;
320 bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
322 // negative nCharPos => reset to first run
323 if( *nCharPos < 0 )
324 mnRunIndex = 0;
326 // return false when all runs completed
327 if( mnRunIndex >= (int)maRuns.size() )
328 return false;
330 int nRunPos0 = maRuns[ mnRunIndex+0 ];
331 int nRunPos1 = maRuns[ mnRunIndex+1 ];
332 *bRightToLeft = (nRunPos0 > nRunPos1);
334 if( *nCharPos < 0 )
336 // get first valid nCharPos in run
337 *nCharPos = nRunPos0;
339 else
341 // advance to next nCharPos for LTR case
342 if( !*bRightToLeft )
343 ++(*nCharPos);
345 // advance to next run if current run is completed
346 if( *nCharPos == nRunPos1 )
348 if( (mnRunIndex += 2) >= (int)maRuns.size() )
349 return false;
350 nRunPos0 = maRuns[ mnRunIndex+0 ];
351 nRunPos1 = maRuns[ mnRunIndex+1 ];
352 *bRightToLeft = (nRunPos0 > nRunPos1);
353 *nCharPos = nRunPos0;
357 // advance to next nCharPos for RTL case
358 if( *bRightToLeft )
359 --(*nCharPos);
361 return true;
364 bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
366 if( mnRunIndex >= (int)maRuns.size() )
367 return false;
369 int nRunPos0 = maRuns[ mnRunIndex+0 ];
370 int nRunPos1 = maRuns[ mnRunIndex+1 ];
371 *bRightToLeft = (nRunPos1 < nRunPos0) ;
372 if( !*bRightToLeft )
374 *nMinRunPos = nRunPos0;
375 *nEndRunPos = nRunPos1;
377 else
379 *nMinRunPos = nRunPos1;
380 *nEndRunPos = nRunPos0;
382 return true;
385 ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr,
386 int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, const LanguageTag& rLanguageTag,
387 vcl::TextLayoutCache const*const pLayoutCache)
389 maLanguageTag( rLanguageTag ),
390 mnFlags( nFlags ),
391 mrStr( rStr ),
392 mnMinCharPos( nMinCharPos ),
393 mnEndCharPos( nEndCharPos ),
394 m_pTextLayoutCache(pLayoutCache),
395 mpDXArray( nullptr ),
396 mnLayoutWidth( 0 ),
397 mnOrientation( 0 )
399 if( mnFlags & SalLayoutFlags::BiDiStrong )
401 // handle strong BiDi mode
403 // do not bother to BiDi analyze strong LTR/RTL
404 // TODO: can we assume these strings do not have unicode control chars?
405 // if not remove the control characters from the runs
406 bool bRTL(mnFlags & SalLayoutFlags::BiDiRtl);
407 AddRun( mnMinCharPos, mnEndCharPos, bRTL );
409 else
411 // handle weak BiDi mode
413 UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
414 if( mnFlags & SalLayoutFlags::BiDiRtl )
415 nLevel = UBIDI_DEFAULT_RTL;
417 // prepare substring for BiDi analysis
418 // TODO: reuse allocated pParaBidi
419 UErrorCode rcI18n = U_ZERO_ERROR;
420 const int nLength = mrStr.getLength();
421 UBiDi* pParaBidi = ubidi_openSized(nLength, 0, &rcI18n);
422 if( !pParaBidi )
423 return;
424 ubidi_setPara(pParaBidi, reinterpret_cast<const UChar *>(mrStr.getStr()), nLength, nLevel, nullptr, &rcI18n);
426 UBiDi* pLineBidi = pParaBidi;
427 int nSubLength = mnEndCharPos - mnMinCharPos;
428 if (nSubLength != nLength)
430 pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
431 ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
434 // run BiDi algorithm
435 const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
436 //maRuns.resize( 2 * nRunCount );
437 for( int i = 0; i < nRunCount; ++i )
439 int32_t nMinPos, nRunLength;
440 const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nRunLength );
441 const int nPos0 = nMinPos + mnMinCharPos;
442 const int nPos1 = nPos0 + nRunLength;
444 const bool bRTL = (nDir == UBIDI_RTL);
445 AddRun( nPos0, nPos1, bRTL );
448 // cleanup BiDi engine
449 if( pLineBidi != pParaBidi )
450 ubidi_close( pLineBidi );
451 ubidi_close( pParaBidi );
454 // prepare calls to GetNextPos/GetNextRun
455 maRuns.ResetPos();
458 // add a run after splitting it up to get rid of control chars
459 void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
461 SAL_WARN_IF( nCharPos0 > nCharPos1, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
463 // remove control characters from runs by splitting them up
464 if( !bRTL )
466 for( int i = nCharPos0; i < nCharPos1; ++i )
467 if( IsControlChar( mrStr[i] ) )
469 // add run until control char
470 maRuns.AddRun( nCharPos0, i, bRTL );
471 nCharPos0 = i + 1;
474 else
476 for( int i = nCharPos1; --i >= nCharPos0; )
477 if( IsControlChar( mrStr[i] ) )
479 // add run until control char
480 maRuns.AddRun( i+1, nCharPos1, bRTL );
481 nCharPos1 = i;
485 // add remainder of run
486 maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
489 bool ImplLayoutArgs::PrepareFallback()
491 // short circuit if no fallback is needed
492 if( maFallbackRuns.IsEmpty() )
494 maRuns.Clear();
495 return false;
498 // convert the fallback requests to layout requests
499 bool bRTL;
500 int nMin, nEnd;
502 // get the individual fallback requests
503 std::vector<int> aPosVector;
504 aPosVector.reserve(mrStr.getLength());
505 maFallbackRuns.ResetPos();
506 for(; maFallbackRuns.GetRun( &nMin, &nEnd, &bRTL ); maFallbackRuns.NextRun() )
507 for( int i = nMin; i < nEnd; ++i )
508 aPosVector.push_back( i );
509 maFallbackRuns.Clear();
511 // sort the individual fallback requests
512 std::sort( aPosVector.begin(), aPosVector.end() );
514 // adjust fallback runs to have the same order and limits of the original runs
515 ImplLayoutRuns aNewRuns;
516 maRuns.ResetPos();
517 for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
519 if( !bRTL) {
520 auto it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
521 for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
522 aNewRuns.AddPos( *it, bRTL );
523 } else {
524 auto it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
525 while( (it != aPosVector.begin()) && (*--it >= nMin) )
526 aNewRuns.AddPos( *it, bRTL );
530 maRuns = aNewRuns; // TODO: use vector<>::swap()
531 maRuns.ResetPos();
532 return true;
535 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
537 bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
538 maRuns.NextRun();
539 return bValid;
542 SalLayout::SalLayout()
543 : mnMinCharPos( -1 ),
544 mnEndCharPos( -1 ),
545 mnLayoutFlags( SalLayoutFlags::NONE ),
546 mnUnitsPerPixel( 1 ),
547 mnOrientation( 0 ),
548 mnRefCount( 1 ),
549 maDrawOffset( 0, 0 )
552 SalLayout::~SalLayout()
555 void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
557 mnMinCharPos = rArgs.mnMinCharPos;
558 mnEndCharPos = rArgs.mnEndCharPos;
559 mnLayoutFlags = rArgs.mnFlags;
560 mnOrientation = rArgs.mnOrientation;
563 void SalLayout::Release() const
565 // TODO: protect when multiple threads can access this
566 if( --mnRefCount > 0 )
567 return;
568 // const_cast because some compilers violate ANSI C++ spec
569 delete this;
572 Point SalLayout::GetDrawPosition( const Point& rRelative ) const
574 Point aPos = maDrawBase;
575 Point aOfs = rRelative + maDrawOffset;
577 if( mnOrientation == 0 )
578 aPos += aOfs;
579 else
581 // cache trigonometric results
582 static int nOldOrientation = 0;
583 static double fCos = 1.0, fSin = 0.0;
584 if( nOldOrientation != mnOrientation )
586 nOldOrientation = mnOrientation;
587 double fRad = mnOrientation * (M_PI / 1800.0);
588 fCos = cos( fRad );
589 fSin = sin( fRad );
592 double fX = aOfs.X();
593 double fY = aOfs.Y();
594 long nX = static_cast<long>( +fCos * fX + fSin * fY );
595 long nY = static_cast<long>( +fCos * fY - fSin * fX );
596 aPos += Point( nX, nY );
599 return aPos;
602 // returns asian kerning values in quarter of character width units
603 // to enable automatic halfwidth substitution for fullwidth punctuation
604 // return value is negative for l, positive for r, zero for neutral
606 // If the range doesn't match in 0x3000 and 0x30FB, please change
607 // also ImplCalcKerning.
609 int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
611 // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
612 static const signed char nTable[0x30] =
614 0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
615 +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
616 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
619 int nResult = 0;
620 if( (c >= 0x3000) && (c < 0x3030) )
621 nResult = nTable[ c - 0x3000 ];
622 else switch( c )
624 case 0x30FB:
625 nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
626 break;
627 case 0x2019: case 0x201D:
628 case 0xFF01: case 0xFF09: case 0xFF0C:
629 case 0xFF1A: case 0xFF1B:
630 nResult = -2;
631 break;
632 case 0x2018: case 0x201C:
633 case 0xFF08:
634 nResult = +2;
635 break;
636 default:
637 break;
640 return nResult;
643 bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
644 basegfx::B2DPolyPolygonVector& rVector ) const
646 bool bAllOk = true;
647 bool bOneOk = false;
649 basegfx::B2DPolyPolygon aGlyphOutline;
651 Point aPos;
652 const GlyphItem* pGlyph;
653 int nStart = 0;
654 while (GetNextGlyphs(1, &pGlyph, aPos, nStart))
656 // get outline of individual glyph, ignoring "empty" glyphs
657 bool bSuccess = rSalGraphics.GetGlyphOutline(*pGlyph, aGlyphOutline);
658 bAllOk &= bSuccess;
659 bOneOk |= bSuccess;
660 // only add non-empty outlines
661 if( bSuccess && (aGlyphOutline.count() > 0) )
663 if( aPos.X() || aPos.Y() )
665 aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
668 // insert outline at correct position
669 rVector.push_back( aGlyphOutline );
673 return (bAllOk && bOneOk);
676 bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, tools::Rectangle& rRect ) const
678 bool bRet = false;
679 rRect.SetEmpty();
681 tools::Rectangle aRectangle;
683 Point aPos;
684 const GlyphItem* pGlyph;
685 int nStart = 0;
686 while (GetNextGlyphs(1, &pGlyph, aPos, nStart))
688 // get bounding rectangle of individual glyph
689 if (rSalGraphics.GetGlyphBoundRect(*pGlyph, aRectangle))
691 // merge rectangle
692 aRectangle += aPos;
693 if (rRect.IsEmpty())
694 rRect = aRectangle;
695 else
696 rRect.Union(aRectangle);
697 bRet = true;
701 return bRet;
704 GenericSalLayout::GenericSalLayout()
707 GenericSalLayout::~GenericSalLayout()
710 void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
712 m_GlyphItems.push_back(rGlyphItem);
715 DeviceCoordinate GenericSalLayout::FillDXArray( DeviceCoordinate* pCharWidths ) const
717 if( pCharWidths )
718 if( !GetCharWidths( pCharWidths ) )
719 return 0;
721 return GetTextWidth();
724 // the text width is the maximum logical extent of all glyphs
725 DeviceCoordinate GenericSalLayout::GetTextWidth() const
727 if( m_GlyphItems.empty() )
728 return 0;
730 // initialize the extent
731 DeviceCoordinate nMinPos = 0;
732 DeviceCoordinate nMaxPos = 0;
734 for (auto const& aGlyphItem : m_GlyphItems)
736 // update the text extent with the glyph extent
737 DeviceCoordinate nXPos = aGlyphItem.maLinearPos.X();
738 if( nMinPos > nXPos )
739 nMinPos = nXPos;
740 nXPos += aGlyphItem.mnNewWidth - aGlyphItem.mnXOffset;
741 if( nMaxPos < nXPos )
742 nMaxPos = nXPos;
745 DeviceCoordinate nWidth = nMaxPos - nMinPos;
746 return nWidth;
749 void GenericSalLayout::Justify( DeviceCoordinate nNewWidth )
751 nNewWidth *= mnUnitsPerPixel;
752 DeviceCoordinate nOldWidth = GetTextWidth();
753 if( !nOldWidth || nNewWidth==nOldWidth )
754 return;
756 if(m_GlyphItems.empty())
758 return;
760 // find rightmost glyph, it won't get stretched
761 std::vector<GlyphItem>::iterator pGlyphIterRight = m_GlyphItems.begin();
762 pGlyphIterRight += m_GlyphItems.size() - 1;
763 std::vector<GlyphItem>::iterator pGlyphIter;
764 // count stretchable glyphs
765 int nStretchable = 0;
766 int nMaxGlyphWidth = 0;
767 for(pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter)
769 if( !pGlyphIter->IsDiacritic() )
770 ++nStretchable;
771 if( nMaxGlyphWidth < pGlyphIter->mnOrigWidth )
772 nMaxGlyphWidth = pGlyphIter->mnOrigWidth;
775 // move rightmost glyph to requested position
776 nOldWidth -= pGlyphIterRight->mnOrigWidth;
777 if( nOldWidth <= 0 )
778 return;
779 if( nNewWidth < nMaxGlyphWidth)
780 nNewWidth = nMaxGlyphWidth;
781 nNewWidth -= pGlyphIterRight->mnOrigWidth;
782 pGlyphIterRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
784 // justify glyph widths and positions
785 int nDiffWidth = nNewWidth - nOldWidth;
786 if( nDiffWidth >= 0) // expanded case
788 // expand width by distributing space between glyphs evenly
789 int nDeltaSum = 0;
790 for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
792 // move glyph to justified position
793 pGlyphIter->maLinearPos.X() += nDeltaSum;
795 // do not stretch non-stretchable glyphs
796 if( pGlyphIter->IsDiacritic() || (nStretchable <= 0) )
797 continue;
799 // distribute extra space equally to stretchable glyphs
800 int nDeltaWidth = nDiffWidth / nStretchable--;
801 nDiffWidth -= nDeltaWidth;
802 pGlyphIter->mnNewWidth += nDeltaWidth;
803 nDeltaSum += nDeltaWidth;
806 else // condensed case
808 // squeeze width by moving glyphs proportionally
809 double fSqueeze = (double)nNewWidth / nOldWidth;
810 if(m_GlyphItems.size() > 1)
812 for( pGlyphIter = m_GlyphItems.begin(); ++pGlyphIter != pGlyphIterRight;)
814 int nX = pGlyphIter->maLinearPos.X() - maBasePoint.X();
815 nX = (int)(nX * fSqueeze);
816 pGlyphIter->maLinearPos.X() = nX + maBasePoint.X();
819 // adjust glyph widths to new positions
820 for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
821 pGlyphIter->mnNewWidth = pGlyphIter[1].maLinearPos.X() - pGlyphIter[0].maLinearPos.X();
825 void GenericSalLayout::ApplyAsianKerning(const OUString& rStr)
827 const int nLength = rStr.getLength();
828 long nOffset = 0;
830 for( std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin(), pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
832 const int n = pGlyphIter->mnCharPos;
833 if( n < nLength - 1)
835 // ignore code ranges that are not affected by asian punctuation compression
836 const sal_Unicode cHere = rStr[n];
837 if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
838 continue;
839 const sal_Unicode cNext = rStr[n+1];
840 if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
841 continue;
843 // calculate compression values
844 const bool bVertical = false;
845 long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
846 long nKernNext = -CalcAsianKerning( cNext, false, bVertical );
848 // apply punctuation compression to logical glyph widths
849 long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
850 if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
852 int nGlyphWidth = pGlyphIter->mnOrigWidth;
853 nDelta = (nDelta * nGlyphWidth + 2) / 4;
854 if( pGlyphIter+1 == pGlyphIterEnd )
855 pGlyphIter->mnNewWidth += nDelta;
856 nOffset += nDelta;
860 // adjust the glyph positions to the new glyph widths
861 if( pGlyphIter+1 != pGlyphIterEnd )
862 pGlyphIter->maLinearPos.X() += nOffset;
866 void GenericSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
868 // initialize result array
869 for (int i = 0; i < nMaxIndex; ++i)
870 pCaretXArray[i] = -1;
872 // calculate caret positions using glyph array
873 for (auto const& aGlyphItem : m_GlyphItems)
875 long nXPos = aGlyphItem.maLinearPos.X();
876 long nXRight = nXPos + aGlyphItem.mnOrigWidth;
877 int n = aGlyphItem.mnCharPos;
878 int nCurrIdx = 2 * (n - mnMinCharPos);
879 // tdf#86399 if this is not the start of a cluster, don't overwrite the caret bounds of the cluster start
880 if (!aGlyphItem.IsClusterStart() && pCaretXArray[nCurrIdx] != -1)
881 continue;
882 if (!aGlyphItem.IsRTLGlyph() )
884 // normal positions for LTR case
885 pCaretXArray[ nCurrIdx ] = nXPos;
886 pCaretXArray[ nCurrIdx+1 ] = nXRight;
888 else
890 // reverse positions for RTL case
891 pCaretXArray[ nCurrIdx ] = nXRight;
892 pCaretXArray[ nCurrIdx+1 ] = nXPos;
897 sal_Int32 GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
899 int nCharCapacity = mnEndCharPos - mnMinCharPos;
900 std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[nCharCapacity]);
901 if (!GetCharWidths(pCharWidths.get()))
902 return -1;
904 DeviceCoordinate nWidth = 0;
905 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
907 nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
908 if( nWidth > nMaxWidth )
909 return i;
910 nWidth += nCharExtra;
913 return -1;
916 int GenericSalLayout::GetNextGlyphs(int nLen, const GlyphItem** pGlyphs,
917 Point& rPos, int& nStart,
918 const PhysicalFontFace** /*pFallbackFonts*/) const
920 std::vector<GlyphItem>::const_iterator pGlyphIter = m_GlyphItems.begin();
921 std::vector<GlyphItem>::const_iterator pGlyphIterEnd = m_GlyphItems.end();
922 pGlyphIter += nStart;
924 // find next glyph in substring
925 for(; pGlyphIter != pGlyphIterEnd; ++nStart, ++pGlyphIter )
927 int n = pGlyphIter->mnCharPos;
928 if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
929 break;
932 // return zero if no more glyph found
933 if( nStart >= (int)m_GlyphItems.size() )
934 return 0;
936 if( pGlyphIter == pGlyphIterEnd )
937 return 0;
939 // calculate absolute position in pixel units
940 Point aRelativePos = pGlyphIter->maLinearPos - maBasePoint;
942 // find more glyphs which can be merged into one drawing instruction
943 int nCount = 0;
944 long nYPos = pGlyphIter->maLinearPos.Y();
945 for(;;)
947 // update return data with glyph info
948 ++nCount;
949 *(pGlyphs++) = &(*pGlyphIter);
951 // break at end of glyph list
952 if( ++nStart >= (int)m_GlyphItems.size() )
953 break;
954 // break when enough glyphs
955 if( nCount >= nLen )
956 break;
958 long nGlyphAdvance = pGlyphIter[1].maLinearPos.X() - pGlyphIter->maLinearPos.X();
959 // stop when next x-position is unexpected
960 if( pGlyphIter->mnOrigWidth != nGlyphAdvance )
961 break;
963 // advance to next glyph
964 ++pGlyphIter;
966 // stop when next y-position is unexpected
967 if( nYPos != pGlyphIter->maLinearPos.Y() )
968 break;
970 // stop when no longer in string
971 int n = pGlyphIter->mnCharPos;
972 if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
973 break;
976 aRelativePos.X() /= mnUnitsPerPixel;
977 aRelativePos.Y() /= mnUnitsPerPixel;
978 rPos = GetDrawPosition( aRelativePos );
980 return nCount;
983 void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
985 if( nStart >= (int)m_GlyphItems.size() )
986 return;
988 std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
989 pGlyphIter += nStart;
991 // the nNewXPos argument determines the new cell position
992 // as RTL-glyphs are right justified in their cell
993 // the cell position needs to be adjusted to the glyph position
994 if( pGlyphIter->IsRTLGlyph() )
995 nNewXPos += pGlyphIter->mnNewWidth - pGlyphIter->mnOrigWidth;
996 // calculate the x-offset to the old position
997 long nXDelta = nNewXPos - pGlyphIter->maLinearPos.X();
998 // adjust all following glyph positions if needed
999 if( nXDelta != 0 )
1001 for( std::vector<GlyphItem>::iterator pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
1003 pGlyphIter->maLinearPos.X() += nXDelta;
1008 void GenericSalLayout::DropGlyph( int nStart )
1010 if( nStart >= (int)m_GlyphItems.size())
1011 return;
1013 std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
1014 pGlyphIter += nStart;
1015 pGlyphIter->maGlyphId = GF_DROPPED;
1016 pGlyphIter->mnCharPos = -1;
1019 void GenericSalLayout::Simplify( bool bIsBase )
1021 const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
1023 // remove dropped glyphs inplace
1024 size_t j = 0;
1025 for(size_t i = 0; i < m_GlyphItems.size(); i++ )
1027 if( m_GlyphItems[i].maGlyphId == nDropMarker )
1028 continue;
1030 if( i != j )
1032 m_GlyphItems[j] = m_GlyphItems[i];
1034 j += 1;
1036 m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
1039 MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout )
1040 : SalLayout()
1041 , mnLevel( 1 )
1042 , mbIncomplete( false )
1044 //maFallbackRuns[0].Clear();
1045 mpFallbackFonts[ 0 ] = nullptr;
1046 mpLayouts[ 0 ] = &rBaseLayout;
1047 mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
1050 void MultiSalLayout::SetIncomplete(bool bIncomplete)
1052 mbIncomplete = bIncomplete;
1053 maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
1056 MultiSalLayout::~MultiSalLayout()
1058 for( int i = 0; i < mnLevel; ++i )
1059 mpLayouts[ i ]->Release();
1062 void MultiSalLayout::AddFallback( SalLayout& rFallback,
1063 ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
1065 if( mnLevel >= MAX_FALLBACK )
1066 return;
1068 mpFallbackFonts[ mnLevel ] = pFallbackFont;
1069 mpLayouts[ mnLevel ] = &rFallback;
1070 maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1071 ++mnLevel;
1074 bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
1076 if( mnLevel <= 1 )
1077 return false;
1078 if (!mbIncomplete)
1079 maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1080 return true;
1083 void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1085 SalLayout::AdjustLayout( rArgs );
1086 ImplLayoutArgs aMultiArgs = rArgs;
1087 std::unique_ptr<DeviceCoordinate[]> pJustificationArray;
1089 if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1091 // for stretched text in a MultiSalLayout the target width needs to be
1092 // distributed by individually adjusting its virtual character widths
1093 DeviceCoordinate nTargetWidth = aMultiArgs.mnLayoutWidth;
1094 nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1095 aMultiArgs.mnLayoutWidth = 0;
1097 // we need to get the original unmodified layouts ready
1098 for( int n = 0; n < mnLevel; ++n )
1099 mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1100 // then we can measure the unmodified metrics
1101 int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1102 pJustificationArray.reset(new DeviceCoordinate[nCharCount]);
1103 FillDXArray( pJustificationArray.get() );
1104 // #i17359# multilayout is not simplified yet, so calculating the
1105 // unjustified width needs handholding; also count the number of
1106 // stretchable virtual char widths
1107 DeviceCoordinate nOrigWidth = 0;
1108 int nStretchable = 0;
1109 for( int i = 0; i < nCharCount; ++i )
1111 // convert array from widths to sum of widths
1112 nOrigWidth += pJustificationArray[i];
1113 if( pJustificationArray[i] > 0 )
1114 ++nStretchable;
1117 // now we are able to distribute the extra width over the virtual char widths
1118 if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1120 DeviceCoordinate nDiffWidth = nTargetWidth - nOrigWidth;
1121 DeviceCoordinate nWidthSum = 0;
1122 for( int i = 0; i < nCharCount; ++i )
1124 DeviceCoordinate nJustWidth = pJustificationArray[i];
1125 if( (nJustWidth > 0) && (nStretchable > 0) )
1127 DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
1128 nJustWidth += nDeltaWidth;
1129 nDiffWidth -= nDeltaWidth;
1130 --nStretchable;
1132 nWidthSum += nJustWidth;
1133 pJustificationArray[i] = nWidthSum;
1135 if( nWidthSum != nTargetWidth )
1136 pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1138 // the justification array is still in base level units
1139 // => convert it to pixel units
1140 if( mnUnitsPerPixel > 1 )
1142 for( int i = 0; i < nCharCount; ++i )
1144 DeviceCoordinate nVal = pJustificationArray[ i ];
1145 nVal += (mnUnitsPerPixel + 1) / 2;
1146 pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1150 // change the mpDXArray temporarily (just for the justification)
1151 aMultiArgs.mpDXArray = pJustificationArray.get();
1155 // Compute rtl flags, since in some scripts glyphs/char order can be
1156 // reversed for a few character sequences e.g. Myanmar
1157 std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1158 rArgs.ResetPos();
1159 bool bRtl;
1160 int nRunStart, nRunEnd;
1161 while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1163 if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1164 vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1166 rArgs.ResetPos();
1168 // prepare "merge sort"
1169 int nStartOld[ MAX_FALLBACK ];
1170 int nStartNew[ MAX_FALLBACK ];
1171 const GlyphItem* pGlyphs[MAX_FALLBACK];
1172 int nValid[ MAX_FALLBACK ] = {0};
1174 Point aPos;
1175 int nLevel = 0, n;
1176 for( n = 0; n < mnLevel; ++n )
1178 // now adjust the individual components
1179 if( n > 0 )
1181 aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1182 aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback;
1184 mpLayouts[n]->AdjustLayout( aMultiArgs );
1186 // remove unused parts of component
1187 if( n > 0 )
1189 if (mbIncomplete && (n == mnLevel-1))
1190 mpLayouts[n]->Simplify( true );
1191 else
1192 mpLayouts[n]->Simplify( false );
1195 // prepare merging components
1196 nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1197 nValid[nLevel] = mpLayouts[n]->GetNextGlyphs(1, &pGlyphs[nLevel], aPos, nStartNew[nLevel]);
1199 if( (n > 0) && !nValid[ nLevel ] )
1201 // an empty fallback layout can be released
1202 mpLayouts[n]->Release();
1204 else
1206 // reshuffle used fallbacks if needed
1207 if( nLevel != n )
1209 mpLayouts[ nLevel ] = mpLayouts[ n ];
1210 mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ];
1211 maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1213 ++nLevel;
1216 mnLevel = nLevel;
1218 // prepare merge the fallback levels
1219 long nXPos = 0;
1220 double fUnitMul = 1.0;
1221 for( n = 0; n < nLevel; ++n )
1222 maFallbackRuns[n].ResetPos();
1223 // get the next codepoint index that needs fallback
1224 int nActiveCharPos = pGlyphs[0]->mnCharPos;
1225 int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1226 // get the end index of the active run
1227 int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
1228 rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1229 int nRunVisibleEndChar = pGlyphs[0]->mnCharPos;
1230 // merge the fallback levels
1231 while( nValid[0] && (nLevel > 0))
1233 // find best fallback level
1234 for( n = 0; n < nLevel; ++n )
1235 if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1236 // fallback level n wins when it requested no further fallback
1237 break;
1238 int nFBLevel = n;
1240 if( n < nLevel )
1242 // use base(n==0) or fallback(n>=1) level
1243 fUnitMul = mnUnitsPerPixel;
1244 fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1245 long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1246 mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1248 else
1250 n = 0; // keep NotDef in base level
1251 fUnitMul = 1.0;
1254 if( n > 0 )
1256 // drop the NotDef glyphs in the base layout run if a fallback run exists
1257 while (
1258 (maFallbackRuns[n-1].PosIsInRun(pGlyphs[0]->mnCharPos)) &&
1259 (!maFallbackRuns[n].PosIsInAnyRun(pGlyphs[0]->mnCharPos))
1262 mpLayouts[0]->DropGlyph( nStartOld[0] );
1263 nStartOld[0] = nStartNew[0];
1264 nValid[0] = mpLayouts[0]->GetNextGlyphs(1, &pGlyphs[0], aPos, nStartNew[0]);
1266 if( !nValid[0] )
1267 break;
1271 // skip to end of layout run and calculate its advance width
1272 DeviceCoordinate nRunAdvance = 0;
1273 bool bKeepNotDef = (nFBLevel >= nLevel);
1274 for(;;)
1276 nRunAdvance += pGlyphs[n]->mnNewWidth;
1278 // proceed to next glyph
1279 nStartOld[n] = nStartNew[n];
1280 int nOrigCharPos = pGlyphs[n]->mnCharPos;
1281 nValid[n] = mpLayouts[n]->GetNextGlyphs(1, &pGlyphs[n], aPos, nStartNew[n]);
1282 // break after last glyph of active layout
1283 if( !nValid[n] )
1285 // performance optimization (when a fallback layout is no longer needed)
1286 if( n >= nLevel-1 )
1287 --nLevel;
1288 break;
1291 //If the next character is one which belongs to the next level, then we
1292 //are finished here for now, and we'll pick up after the next level has
1293 //been processed
1294 if ((n+1 < nLevel) && (pGlyphs[n]->mnCharPos != nOrigCharPos))
1296 if (nOrigCharPos < pGlyphs[n]->mnCharPos)
1298 if (pGlyphs[n+1]->mnCharPos > nOrigCharPos && (pGlyphs[n+1]->mnCharPos < pGlyphs[n]->mnCharPos))
1299 break;
1301 else if (nOrigCharPos > pGlyphs[n]->mnCharPos)
1303 if (pGlyphs[n+1]->mnCharPos > pGlyphs[n]->mnCharPos && (pGlyphs[n+1]->mnCharPos < nOrigCharPos))
1304 break;
1308 // break at end of layout run
1309 if( n > 0 )
1311 // skip until end of fallback run
1312 if (!maFallbackRuns[n-1].PosIsInRun(pGlyphs[n]->mnCharPos))
1313 break;
1315 else
1317 // break when a fallback is needed and available
1318 bool bNeedFallback = maFallbackRuns[0].PosIsInRun(pGlyphs[0]->mnCharPos);
1319 if( bNeedFallback )
1320 if (!maFallbackRuns[nLevel-1].PosIsInRun(pGlyphs[0]->mnCharPos))
1321 break;
1322 // break when change from resolved to unresolved base layout run
1323 if( bKeepNotDef && !bNeedFallback )
1324 { maFallbackRuns[0].NextRun(); break; }
1325 bKeepNotDef = bNeedFallback;
1327 // check for reordered glyphs
1328 if (aMultiArgs.mpDXArray &&
1329 nRunVisibleEndChar < mnEndCharPos &&
1330 nRunVisibleEndChar >= mnMinCharPos &&
1331 pGlyphs[n]->mnCharPos < mnEndCharPos &&
1332 pGlyphs[n]->mnCharPos >= mnMinCharPos)
1334 if (vRtl[nActiveCharPos - mnMinCharPos])
1336 if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1337 >= aMultiArgs.mpDXArray[pGlyphs[n]->mnCharPos - mnMinCharPos])
1339 nRunVisibleEndChar = pGlyphs[n]->mnCharPos;
1342 else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1343 <= aMultiArgs.mpDXArray[pGlyphs[n]->mnCharPos - mnMinCharPos])
1345 nRunVisibleEndChar = pGlyphs[n]->mnCharPos;
1350 // if a justification array is available
1351 // => use it directly to calculate the corresponding run width
1352 if( aMultiArgs.mpDXArray )
1354 // the run advance is the width from the first char
1355 // in the run to the first char in the next run
1356 nRunAdvance = 0;
1357 nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1358 if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
1360 if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1361 nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1362 if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1363 nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
1365 else
1367 if (nRunVisibleEndChar >= mnMinCharPos)
1368 nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1369 if (nLastRunEndChar >= mnMinCharPos)
1370 nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
1372 nLastRunEndChar = nRunVisibleEndChar;
1373 nRunVisibleEndChar = pGlyphs[0]->mnCharPos;
1374 // the requested width is still in pixel units
1375 // => convert it to base level font units
1376 nRunAdvance *= mnUnitsPerPixel;
1378 else
1380 // the measured width is still in fallback font units
1381 // => convert it to base level font units
1382 if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1383 nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
1386 // calculate new x position (in base level units)
1387 nXPos += nRunAdvance;
1389 // prepare for next fallback run
1390 nActiveCharPos = pGlyphs[0]->mnCharPos;
1391 // it essential that the runs don't get ahead of themselves and in the
1392 // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1393 // have already been reached on the base level
1394 for( int i = nFBLevel; --i >= 0;)
1396 if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1398 if (bRtl)
1400 if (nRunStart > nActiveCharPos)
1401 maFallbackRuns[i].NextRun();
1403 else
1405 if (nRunEnd <= nActiveCharPos)
1406 maFallbackRuns[i].NextRun();
1412 mpLayouts[0]->Simplify( true );
1415 void MultiSalLayout::InitFont() const
1417 if( mnLevel > 0 )
1418 mpLayouts[0]->InitFont();
1421 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1423 for( int i = mnLevel; --i >= 0; )
1425 SalLayout& rLayout = *mpLayouts[ i ];
1426 rLayout.DrawBase() += maDrawBase;
1427 rLayout.DrawOffset() += maDrawOffset;
1428 rLayout.InitFont();
1429 rLayout.DrawText( rGraphics );
1430 rLayout.DrawOffset() -= maDrawOffset;
1431 rLayout.DrawBase() -= maDrawBase;
1433 // NOTE: now the baselevel font is active again
1436 sal_Int32 MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1438 if( mnLevel <= 0 )
1439 return -1;
1440 if( mnLevel == 1 )
1441 return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1443 int nCharCount = mnEndCharPos - mnMinCharPos;
1444 std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[2 * nCharCount]);
1445 mpLayouts[0]->FillDXArray( pCharWidths.get() );
1447 for( int n = 1; n < mnLevel; ++n )
1449 SalLayout& rLayout = *mpLayouts[ n ];
1450 rLayout.FillDXArray( &pCharWidths[nCharCount] );
1451 double fUnitMul = mnUnitsPerPixel;
1452 fUnitMul /= rLayout.GetUnitsPerPixel();
1453 for( int i = 0; i < nCharCount; ++i )
1455 if( pCharWidths[ i ] == 0 )
1457 DeviceCoordinate w = pCharWidths[ i + nCharCount ];
1458 w = (DeviceCoordinate)(w * fUnitMul + 0.5);
1459 pCharWidths[ i ] = w;
1464 DeviceCoordinate nWidth = 0;
1465 for( int i = 0; i < nCharCount; ++i )
1467 nWidth += pCharWidths[ i ] * nFactor;
1468 if( nWidth > nMaxWidth )
1469 return (i + mnMinCharPos);
1470 nWidth += nCharExtra;
1473 return -1;
1476 DeviceCoordinate MultiSalLayout::FillDXArray( DeviceCoordinate* pCharWidths ) const
1478 DeviceCoordinate nMaxWidth = 0;
1480 // prepare merging of fallback levels
1481 std::unique_ptr<DeviceCoordinate[]> pTempWidths;
1482 const int nCharCount = mnEndCharPos - mnMinCharPos;
1483 if( pCharWidths )
1485 for( int i = 0; i < nCharCount; ++i )
1486 pCharWidths[i] = 0;
1487 pTempWidths.reset(new DeviceCoordinate[nCharCount]);
1490 for( int n = mnLevel; --n >= 0; )
1492 // query every fallback level
1493 DeviceCoordinate nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths.get() );
1494 if( !nTextWidth )
1495 continue;
1496 // merge results from current level
1497 double fUnitMul = mnUnitsPerPixel;
1498 fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1499 nTextWidth = (DeviceCoordinate)(nTextWidth * fUnitMul + 0.5);
1500 if( nMaxWidth < nTextWidth )
1501 nMaxWidth = nTextWidth;
1502 if( !pCharWidths )
1503 continue;
1504 // calculate virtual char widths using most probable fallback layout
1505 for( int i = 0; i < nCharCount; ++i )
1507 // #i17359# restriction:
1508 // one char cannot be resolved from different fallbacks
1509 if( pCharWidths[i] != 0 )
1510 continue;
1511 DeviceCoordinate nCharWidth = pTempWidths[i];
1512 if( !nCharWidth )
1513 continue;
1514 nCharWidth = (DeviceCoordinate)(nCharWidth * fUnitMul + 0.5);
1515 pCharWidths[i] = nCharWidth;
1519 return nMaxWidth;
1522 void MultiSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
1524 SalLayout& rLayout = *mpLayouts[ 0 ];
1525 rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
1527 if( mnLevel > 1 )
1529 std::unique_ptr<long[]> const pTempPos(new long[nMaxIndex]);
1530 for( int n = 1; n < mnLevel; ++n )
1532 mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos.get() );
1533 double fUnitMul = mnUnitsPerPixel;
1534 fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1535 for( int i = 0; i < nMaxIndex; ++i )
1536 if( pTempPos[i] >= 0 )
1538 long w = pTempPos[i];
1539 w = static_cast<long>(w*fUnitMul + 0.5);
1540 pCaretXArray[i] = w;
1546 int MultiSalLayout::GetNextGlyphs(int nLen, const GlyphItem** pGlyphs,
1547 Point& rPos, int& nStart,
1548 const PhysicalFontFace** pFallbackFonts) const
1550 // for multi-level fallback only single glyphs should be used
1551 if( mnLevel > 1 && nLen > 1 )
1552 nLen = 1;
1554 // NOTE: nStart is tagged with current font index
1555 int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
1556 nStart &= ~GF_FONTMASK;
1557 for(; nLevel < mnLevel; ++nLevel, nStart=0 )
1559 SalLayout& rLayout = *mpLayouts[ nLevel ];
1560 rLayout.InitFont();
1561 int nRetVal = rLayout.GetNextGlyphs(nLen, pGlyphs, rPos, nStart);
1562 if( nRetVal )
1564 int nFontTag = nLevel << GF_FONTSHIFT;
1565 nStart |= nFontTag;
1566 for( int i = 0; i < nRetVal; ++i )
1568 // FIXME: This cast is ugly!
1569 const_cast<GlyphItem*>(pGlyphs[i])->mnFallbackLevel = nLevel;
1570 if( pFallbackFonts )
1572 pFallbackFonts[ i ] = mpFallbackFonts[ nLevel ];
1575 rPos += maDrawBase;
1576 rPos += maDrawOffset;
1577 return nRetVal;
1581 // #111016# reset to base level font when done
1582 mpLayouts[0]->InitFont();
1583 return 0;
1586 bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
1587 basegfx::B2DPolyPolygonVector& rPPV ) const
1589 bool bRet = false;
1591 for( int i = mnLevel; --i >= 0; )
1593 SalLayout& rLayout = *mpLayouts[ i ];
1594 rLayout.DrawBase() = maDrawBase;
1595 rLayout.DrawOffset() += maDrawOffset;
1596 rLayout.InitFont();
1597 bRet |= rLayout.GetOutline( rGraphics, rPPV );
1598 rLayout.DrawOffset() -= maDrawOffset;
1601 return bRet;
1604 bool MultiSalLayout::IsKashidaPosValid(int nCharPos) const
1606 // Check the base layout
1607 bool bValid = mpLayouts[0]->IsKashidaPosValid(nCharPos);
1609 // If base layout returned false, it might be because the character was not
1610 // supported there, so we check fallback layouts.
1611 if (!bValid)
1613 for (int i = 1; i < mnLevel; ++i)
1615 // - 1 because there is no fallback run for the base layout, IIUC.
1616 if (maFallbackRuns[i - 1].PosIsInAnyRun(nCharPos))
1618 bValid = mpLayouts[i]->IsKashidaPosValid(nCharPos);
1619 break;
1624 return bValid;
1627 std::shared_ptr<vcl::TextLayoutCache> SalLayout::CreateTextLayoutCache(
1628 OUString const&) const
1630 return nullptr; // by default, nothing to cache
1633 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */