1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
23 #include <sal/config.h>
24 #include <sal/log.hxx>
31 #include <sallayout.hxx>
32 #include <basegfx/polygon/b2dpolypolygon.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 #include <i18nlangtag/lang.h>
37 #include <vcl/svapp.hxx>
39 #include <unicode/ubidi.h>
40 #include <unicode/uchar.h>
45 #include <impglyphitem.hxx>
48 #define GF_FONTMASK 0xF0000000
49 #define GF_FONTSHIFT 28
52 std::ostream
&operator <<(std::ostream
& s
, ImplLayoutArgs
const &rArgs
)
57 s
<< "ImplLayoutArgs{";
60 if (rArgs
.mnFlags
== SalLayoutFlags::NONE
)
65 #define TEST(x) if (rArgs.mnFlags & SalLayoutFlags::x) { if (need_or) s << "|"; s << #x; need_or = true; }
72 TEST(KashidaJustification
);
78 const int nLength
= rArgs
.mrStr
.getLength();
80 s
<< ",Length=" << nLength
;
81 s
<< ",MinCharPos=" << rArgs
.mnMinCharPos
;
82 s
<< ",EndCharPos=" << rArgs
.mnEndCharPos
;
88 for (int i
= 0; i
< lim
; i
++) {
89 if (rArgs
.mrStr
[i
] == '\n')
91 else if (rArgs
.mrStr
[i
] < ' ' || (rArgs
.mrStr
[i
] >= 0x7F && rArgs
.mrStr
[i
] <= 0xFF))
92 s
<< "\\0x" << std::hex
<< std::setw(2) << std::setfill('0') << static_cast<int>(rArgs
.mrStr
[i
]) << std::setfill(' ') << std::setw(1) << std::dec
;
93 else if (rArgs
.mrStr
[i
] < 0x7F)
94 s
<< static_cast<char>(rArgs
.mrStr
[i
]);
96 s
<< "\\u" << std::hex
<< std::setw(4) << std::setfill('0') << static_cast<int>(rArgs
.mrStr
[i
]) << std::setfill(' ') << std::setw(1) << std::dec
;
103 if (rArgs
.mpDXArray
) {
105 int count
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
109 for (int i
= 0; i
< lim
; i
++) {
110 s
<< rArgs
.mpDXArray
[i
];
117 s
<< rArgs
.mpDXArray
[count
-1];
123 s
<< ",LayoutWidth=" << rArgs
.mnLayoutWidth
;
131 sal_UCS4
GetMirroredChar( sal_UCS4 nChar
)
133 nChar
= u_charMirror( nChar
);
137 sal_UCS4
GetLocalizedChar( sal_UCS4 nChar
, LanguageType eLang
)
139 // currently only conversion from ASCII digits is interesting
140 if( (nChar
< '0') || ('9' < nChar
) )
144 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
145 // CAVEAT! To some like Mongolian MS assigned the same primary language
146 // although the script type is different!
147 LanguageType pri
= primary(eLang
);
148 if( pri
== primary(LANGUAGE_ARABIC_SAUDI_ARABIA
) )
149 nOffset
= 0x0660 - '0'; // arabic-indic digits
151 primary(LANGUAGE_FARSI
),
152 primary(LANGUAGE_URDU_PAKISTAN
),
153 primary(LANGUAGE_PUNJABI
), //???
154 primary(LANGUAGE_SINDHI
)))
155 nOffset
= 0x06F0 - '0'; // eastern arabic-indic digits
156 else if ( pri
== primary(LANGUAGE_BENGALI
) )
157 nOffset
= 0x09E6 - '0'; // bengali
158 else if ( pri
== primary(LANGUAGE_HINDI
) )
159 nOffset
= 0x0966 - '0'; // devanagari
161 primary(LANGUAGE_AMHARIC_ETHIOPIA
),
162 primary(LANGUAGE_TIGRIGNA_ETHIOPIA
)))
164 nOffset
= 0x1369 - '0'; // ethiopic
165 else if ( pri
== primary(LANGUAGE_GUJARATI
) )
166 nOffset
= 0x0AE6 - '0'; // gujarati
167 #ifdef LANGUAGE_GURMUKHI // TODO case:
168 else if ( pri
== primary(LANGUAGE_GURMUKHI
) )
169 nOffset
= 0x0A66 - '0'; // gurmukhi
171 else if ( pri
== primary(LANGUAGE_KANNADA
) )
172 nOffset
= 0x0CE6 - '0'; // kannada
173 else if ( pri
== primary(LANGUAGE_KHMER
))
174 nOffset
= 0x17E0 - '0'; // khmer
175 else if ( pri
== primary(LANGUAGE_LAO
) )
176 nOffset
= 0x0ED0 - '0'; // lao
177 else if ( pri
== primary(LANGUAGE_MALAYALAM
) )
178 nOffset
= 0x0D66 - '0'; // malayalam
179 else if ( pri
== primary(LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
))
182 LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
,
183 LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
,
184 LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
))
185 nOffset
= 0x1810 - '0'; // mongolian
187 nOffset
= 0; // mongolian cyrillic
189 else if ( pri
== primary(LANGUAGE_BURMESE
) )
190 nOffset
= 0x1040 - '0'; // myanmar
191 else if ( pri
== primary(LANGUAGE_ODIA
) )
192 nOffset
= 0x0B66 - '0'; // odia
193 else if ( pri
== primary(LANGUAGE_TAMIL
) )
194 nOffset
= 0x0BE7 - '0'; // tamil
195 else if ( pri
== primary(LANGUAGE_TELUGU
) )
196 nOffset
= 0x0C66 - '0'; // telugu
197 else if ( pri
== primary(LANGUAGE_THAI
) )
198 nOffset
= 0x0E50 - '0'; // thai
199 else if ( pri
== primary(LANGUAGE_TIBETAN
) )
200 nOffset
= 0x0F20 - '0'; // tibetan
210 static bool IsControlChar( sal_UCS4 cChar
)
212 // C0 control characters
213 if( (0x0001 <= cChar
) && (cChar
<= 0x001F) )
215 // formatting characters
216 if( (0x200E <= cChar
) && (cChar
<= 0x200F) )
218 if( (0x2028 <= cChar
) && (cChar
<= 0x202E) )
220 // deprecated formatting characters
221 if( (0x206A <= cChar
) && (cChar
<= 0x206F) )
223 if( 0x2060 == cChar
)
225 // byte order markers and invalid unicode
226 if( (cChar
== 0xFEFF) || (cChar
== 0xFFFE) || (cChar
== 0xFFFF) )
231 void ImplLayoutRuns::AddPos( int nCharPos
, bool bRTL
)
233 // check if charpos could extend current run
234 int nIndex
= maRuns
.size();
237 int nRunPos0
= maRuns
[ nIndex
-2 ];
238 int nRunPos1
= maRuns
[ nIndex
-1 ];
239 if( ((nCharPos
+ int(bRTL
)) == nRunPos1
) && ((nRunPos0
> nRunPos1
) == bRTL
) )
241 // extend current run by new charpos
242 maRuns
[ nIndex
-1 ] = nCharPos
+ int(!bRTL
);
245 // ignore new charpos when it is in current run
246 if( (nRunPos0
<= nCharPos
) && (nCharPos
< nRunPos1
) )
248 if( (nRunPos1
<= nCharPos
) && (nCharPos
< nRunPos0
) )
252 // else append a new run consisting of the new charpos
253 maRuns
.push_back( nCharPos
+ (bRTL
? 1 : 0) );
254 maRuns
.push_back( nCharPos
+ (bRTL
? 0 : 1) );
257 void ImplLayoutRuns::AddRun( int nCharPos0
, int nCharPos1
, bool bRTL
)
259 if( nCharPos0
== nCharPos1
)
263 if( bRTL
== (nCharPos0
< nCharPos1
) )
265 int nTemp
= nCharPos0
;
266 nCharPos0
= nCharPos1
;
270 if (maRuns
.size() >= 2 && nCharPos0
== maRuns
[maRuns
.size() - 2] && nCharPos1
== maRuns
[maRuns
.size() - 1])
272 //this run is the same as the last
277 maRuns
.push_back( nCharPos0
);
278 maRuns
.push_back( nCharPos1
);
281 bool ImplLayoutRuns::PosIsInRun( int nCharPos
) const
283 if( mnRunIndex
>= static_cast<int>(maRuns
.size()) )
286 int nMinCharPos
= maRuns
[ mnRunIndex
+0 ];
287 int nEndCharPos
= maRuns
[ mnRunIndex
+1 ];
288 if( nMinCharPos
> nEndCharPos
) // reversed in RTL case
290 int nTemp
= nMinCharPos
;
291 nMinCharPos
= nEndCharPos
;
295 if( nCharPos
< nMinCharPos
)
297 if( nCharPos
>= nEndCharPos
)
302 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos
) const
305 int nRunIndex
= mnRunIndex
;
307 ImplLayoutRuns
*pThis
= const_cast<ImplLayoutRuns
*>(this);
311 for (size_t i
= 0; i
< maRuns
.size(); i
+=2)
313 bRet
= PosIsInRun( nCharPos
);
319 pThis
->mnRunIndex
= nRunIndex
;
323 bool ImplLayoutRuns::GetNextPos( int* nCharPos
, bool* bRightToLeft
)
325 // negative nCharPos => reset to first run
329 // return false when all runs completed
330 if( mnRunIndex
>= static_cast<int>(maRuns
.size()) )
333 int nRunPos0
= maRuns
[ mnRunIndex
+0 ];
334 int nRunPos1
= maRuns
[ mnRunIndex
+1 ];
335 *bRightToLeft
= (nRunPos0
> nRunPos1
);
339 // get first valid nCharPos in run
340 *nCharPos
= nRunPos0
;
344 // advance to next nCharPos for LTR case
348 // advance to next run if current run is completed
349 if( *nCharPos
== nRunPos1
)
351 if( (mnRunIndex
+= 2) >= static_cast<int>(maRuns
.size()) )
353 nRunPos0
= maRuns
[ mnRunIndex
+0 ];
354 nRunPos1
= maRuns
[ mnRunIndex
+1 ];
355 *bRightToLeft
= (nRunPos0
> nRunPos1
);
356 *nCharPos
= nRunPos0
;
360 // advance to next nCharPos for RTL case
367 bool ImplLayoutRuns::GetRun( int* nMinRunPos
, int* nEndRunPos
, bool* bRightToLeft
) const
369 if( mnRunIndex
>= static_cast<int>(maRuns
.size()) )
372 int nRunPos0
= maRuns
[ mnRunIndex
+0 ];
373 int nRunPos1
= maRuns
[ mnRunIndex
+1 ];
374 *bRightToLeft
= (nRunPos1
< nRunPos0
) ;
377 *nMinRunPos
= nRunPos0
;
378 *nEndRunPos
= nRunPos1
;
382 *nMinRunPos
= nRunPos1
;
383 *nEndRunPos
= nRunPos0
;
388 ImplLayoutArgs::ImplLayoutArgs(const OUString
& rStr
,
389 int nMinCharPos
, int nEndCharPos
, SalLayoutFlags nFlags
, const LanguageTag
& rLanguageTag
,
390 vcl::TextLayoutCache
const*const pLayoutCache
)
392 maLanguageTag( rLanguageTag
),
395 mnMinCharPos( nMinCharPos
),
396 mnEndCharPos( nEndCharPos
),
397 m_pTextLayoutCache(pLayoutCache
),
398 mpDXArray( nullptr ),
402 if( mnFlags
& SalLayoutFlags::BiDiStrong
)
404 // handle strong BiDi mode
406 // do not bother to BiDi analyze strong LTR/RTL
407 // TODO: can we assume these strings do not have unicode control chars?
408 // if not remove the control characters from the runs
409 bool bRTL(mnFlags
& SalLayoutFlags::BiDiRtl
);
410 AddRun( mnMinCharPos
, mnEndCharPos
, bRTL
);
414 // handle weak BiDi mode
415 UBiDiLevel nLevel
= (mnFlags
& SalLayoutFlags::BiDiRtl
)? 1 : 0;
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
);
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
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
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
);
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
);
485 // add remainder of run
486 maRuns
.AddRun( nCharPos0
, nCharPos1
, bRTL
);
489 bool ImplLayoutArgs::PrepareFallback(const SalLayoutGlyphsImpl
* pGlyphsImpl
)
491 // Generate runs with pre-calculated glyph items instead maFallbackRuns.
492 if( pGlyphsImpl
!= nullptr )
495 maFallbackRuns
.Clear();
497 for (auto const& aGlyphItem
: *pGlyphsImpl
)
499 for(int i
= aGlyphItem
.charPos(); i
< aGlyphItem
.charPos() + aGlyphItem
.charCount(); ++i
)
500 maRuns
.AddPos(i
, aGlyphItem
.IsRTLGlyph());
503 return !maRuns
.IsEmpty();
506 // short circuit if no fallback is needed
507 if( maFallbackRuns
.IsEmpty() )
513 // convert the fallback requests to layout requests
517 // get the individual fallback requests
518 std::vector
<int> aPosVector
;
519 aPosVector
.reserve(mrStr
.getLength());
520 maFallbackRuns
.ResetPos();
521 for(; maFallbackRuns
.GetRun( &nMin
, &nEnd
, &bRTL
); maFallbackRuns
.NextRun() )
522 for( int i
= nMin
; i
< nEnd
; ++i
)
523 aPosVector
.push_back( i
);
524 maFallbackRuns
.Clear();
526 // sort the individual fallback requests
527 std::sort( aPosVector
.begin(), aPosVector
.end() );
529 // adjust fallback runs to have the same order and limits of the original runs
530 ImplLayoutRuns aNewRuns
;
532 for(; maRuns
.GetRun( &nMin
, &nEnd
, &bRTL
); maRuns
.NextRun() )
535 auto it
= std::lower_bound( aPosVector
.begin(), aPosVector
.end(), nMin
);
536 for(; (it
!= aPosVector
.end()) && (*it
< nEnd
); ++it
)
537 aNewRuns
.AddPos( *it
, bRTL
);
539 auto it
= std::upper_bound( aPosVector
.begin(), aPosVector
.end(), nEnd
);
540 while( (it
!= aPosVector
.begin()) && (*--it
>= nMin
) )
541 aNewRuns
.AddPos( *it
, bRTL
);
545 maRuns
= aNewRuns
; // TODO: use vector<>::swap()
550 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos
, int* nEndRunPos
, bool* bRTL
)
552 bool bValid
= maRuns
.GetRun( nMinRunPos
, nEndRunPos
, bRTL
);
557 SalLayout::SalLayout()
558 : mnMinCharPos( -1 ),
560 mnUnitsPerPixel( 1 ),
565 SalLayout::~SalLayout()
568 void SalLayout::AdjustLayout( ImplLayoutArgs
& rArgs
)
570 mnMinCharPos
= rArgs
.mnMinCharPos
;
571 mnEndCharPos
= rArgs
.mnEndCharPos
;
572 mnOrientation
= rArgs
.mnOrientation
;
575 Point
SalLayout::GetDrawPosition( const Point
& rRelative
) const
577 Point aPos
= maDrawBase
;
578 Point aOfs
= rRelative
+ maDrawOffset
;
580 if( mnOrientation
== 0_deg10
)
584 // cache trigonometric results
585 static Degree10
nOldOrientation(0);
586 static double fCos
= 1.0, fSin
= 0.0;
587 if( nOldOrientation
!= mnOrientation
)
589 nOldOrientation
= mnOrientation
;
590 double fRad
= toRadians(mnOrientation
);
595 double fX
= aOfs
.X();
596 double fY
= aOfs
.Y();
597 tools::Long nX
= static_cast<tools::Long
>( +fCos
* fX
+ fSin
* fY
);
598 tools::Long nY
= static_cast<tools::Long
>( +fCos
* fY
- fSin
* fX
);
599 aPos
+= Point( nX
, nY
);
605 bool SalLayout::GetOutline(basegfx::B2DPolyPolygonVector
& rVector
) const
610 basegfx::B2DPolyPolygon aGlyphOutline
;
613 const GlyphItem
* pGlyph
;
615 while (GetNextGlyph(&pGlyph
, aPos
, nStart
))
617 // get outline of individual glyph, ignoring "empty" glyphs
618 bool bSuccess
= pGlyph
->GetGlyphOutline(aGlyphOutline
);
621 // only add non-empty outlines
622 if( bSuccess
&& (aGlyphOutline
.count() > 0) )
624 if( aPos
.X() || aPos
.Y() )
626 aGlyphOutline
.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos
.X(), aPos
.Y()));
629 // insert outline at correct position
630 rVector
.push_back( aGlyphOutline
);
634 return (bAllOk
&& bOneOk
);
637 bool SalLayout::GetBoundRect(tools::Rectangle
& rRect
) const
642 tools::Rectangle aRectangle
;
645 const GlyphItem
* pGlyph
;
647 while (GetNextGlyph(&pGlyph
, aPos
, nStart
))
649 // get bounding rectangle of individual glyph
650 if (pGlyph
->GetGlyphBoundRect(aRectangle
))
657 rRect
.Union(aRectangle
);
665 SalLayoutGlyphs
SalLayout::GetGlyphs() const
667 return SalLayoutGlyphs(); // invalid
670 DeviceCoordinate
GenericSalLayout::FillDXArray( DeviceCoordinate
* pCharWidths
) const
673 GetCharWidths(pCharWidths
);
675 return GetTextWidth();
678 // the text width is the maximum logical extent of all glyphs
679 DeviceCoordinate
GenericSalLayout::GetTextWidth() const
681 if (!m_GlyphItems
.IsValid())
684 // initialize the extent
685 DeviceCoordinate nMinPos
= 0;
686 DeviceCoordinate nMaxPos
= 0;
688 for (auto const& aGlyphItem
: m_GlyphItems
)
690 // update the text extent with the glyph extent
691 DeviceCoordinate nXPos
= aGlyphItem
.m_aLinearPos
.getX();
692 if( nMinPos
> nXPos
)
694 nXPos
+= aGlyphItem
.m_nNewWidth
- aGlyphItem
.xOffset();
695 if( nMaxPos
< nXPos
)
699 DeviceCoordinate nWidth
= nMaxPos
- nMinPos
;
703 void GenericSalLayout::Justify( DeviceCoordinate nNewWidth
)
705 nNewWidth
*= mnUnitsPerPixel
;
706 DeviceCoordinate nOldWidth
= GetTextWidth();
707 if( !nOldWidth
|| nNewWidth
==nOldWidth
)
710 if (!m_GlyphItems
.IsValid())
714 // find rightmost glyph, it won't get stretched
715 std::vector
<GlyphItem
>::iterator pGlyphIterRight
= m_GlyphItems
.begin();
716 pGlyphIterRight
+= m_GlyphItems
.size() - 1;
717 std::vector
<GlyphItem
>::iterator pGlyphIter
;
718 // count stretchable glyphs
719 int nStretchable
= 0;
720 int nMaxGlyphWidth
= 0;
721 for(pGlyphIter
= m_GlyphItems
.begin(); pGlyphIter
!= pGlyphIterRight
; ++pGlyphIter
)
723 if( !pGlyphIter
->IsDiacritic() )
725 if (nMaxGlyphWidth
< pGlyphIter
->origWidth())
726 nMaxGlyphWidth
= pGlyphIter
->origWidth();
729 // move rightmost glyph to requested position
730 nOldWidth
-= pGlyphIterRight
->origWidth();
733 if( nNewWidth
< nMaxGlyphWidth
)
734 nNewWidth
= nMaxGlyphWidth
;
735 nNewWidth
-= pGlyphIterRight
->origWidth();
736 pGlyphIterRight
->m_aLinearPos
.setX( nNewWidth
);
738 // justify glyph widths and positions
739 int nDiffWidth
= nNewWidth
- nOldWidth
;
740 if( nDiffWidth
>= 0) // expanded case
742 // expand width by distributing space between glyphs evenly
744 for( pGlyphIter
= m_GlyphItems
.begin(); pGlyphIter
!= pGlyphIterRight
; ++pGlyphIter
)
746 // move glyph to justified position
747 pGlyphIter
->m_aLinearPos
.AdjustX(nDeltaSum
);
749 // do not stretch non-stretchable glyphs
750 if( pGlyphIter
->IsDiacritic() || (nStretchable
<= 0) )
753 // distribute extra space equally to stretchable glyphs
754 int nDeltaWidth
= nDiffWidth
/ nStretchable
--;
755 nDiffWidth
-= nDeltaWidth
;
756 pGlyphIter
->m_nNewWidth
+= nDeltaWidth
;
757 nDeltaSum
+= nDeltaWidth
;
760 else // condensed case
762 // squeeze width by moving glyphs proportionally
763 double fSqueeze
= static_cast<double>(nNewWidth
) / nOldWidth
;
764 if(m_GlyphItems
.size() > 1)
766 for( pGlyphIter
= m_GlyphItems
.begin(); ++pGlyphIter
!= pGlyphIterRight
;)
768 int nX
= pGlyphIter
->m_aLinearPos
.getX();
769 nX
= static_cast<int>(nX
* fSqueeze
);
770 pGlyphIter
->m_aLinearPos
.setX( nX
);
773 // adjust glyph widths to new positions
774 for( pGlyphIter
= m_GlyphItems
.begin(); pGlyphIter
!= pGlyphIterRight
; ++pGlyphIter
)
775 pGlyphIter
->m_nNewWidth
= pGlyphIter
[1].m_aLinearPos
.getX() - pGlyphIter
[0].m_aLinearPos
.getX();
779 // returns asian kerning values in quarter of character width units
780 // to enable automatic halfwidth substitution for fullwidth punctuation
781 // return value is negative for l, positive for r, zero for neutral
782 // TODO: handle vertical layout as proposed in commit 43bf2ad49c2b3989bbbe893e4fee2e032a3920f5?
783 static int lcl_CalcAsianKerning(sal_UCS4 c
, bool bLeft
)
785 // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
786 static const signed char nTable
[0x30] =
788 0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
789 +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
790 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
794 if( (c
>= 0x3000) && (c
< 0x3030) )
795 nResult
= nTable
[ c
- 0x3000 ];
799 nResult
= bLeft
? -1 : +1; // 25% left/right/top/bottom
801 case 0x2019: case 0x201D:
802 case 0xFF01: case 0xFF09: case 0xFF0C:
803 case 0xFF1A: case 0xFF1B:
806 case 0x2018: case 0x201C:
817 static bool lcl_CanApplyAsianKerning(sal_Unicode cp
)
819 return (0x3000 == (cp
& 0xFF00)) || (0xFF00 == (cp
& 0xFF00)) || (0x2010 == (cp
& 0xFFF0));
822 void GenericSalLayout::ApplyAsianKerning(const OUString
& rStr
)
824 const int nLength
= rStr
.getLength();
825 tools::Long nOffset
= 0;
827 for (std::vector
<GlyphItem
>::iterator pGlyphIter
= m_GlyphItems
.begin(),
828 pGlyphIterEnd
= m_GlyphItems
.end();
829 pGlyphIter
!= pGlyphIterEnd
; ++pGlyphIter
)
831 const int n
= pGlyphIter
->charPos();
834 // ignore code ranges that are not affected by asian punctuation compression
835 const sal_Unicode cCurrent
= rStr
[n
];
836 if (!lcl_CanApplyAsianKerning(cCurrent
))
838 const sal_Unicode cNext
= rStr
[n
+ 1];
839 if (!lcl_CanApplyAsianKerning(cNext
))
842 // calculate compression values
843 const int nKernCurrent
= +lcl_CalcAsianKerning(cCurrent
, true);
844 if (nKernCurrent
== 0)
846 const int nKernNext
= -lcl_CalcAsianKerning(cNext
, false);
850 // apply punctuation compression to logical glyph widths
851 int nDelta
= (nKernCurrent
< nKernNext
) ? nKernCurrent
: nKernNext
;
854 nDelta
= (nDelta
* pGlyphIter
->origWidth() + 2) / 4;
855 if( pGlyphIter
+1 == pGlyphIterEnd
)
856 pGlyphIter
->m_nNewWidth
+= nDelta
;
861 // adjust the glyph positions to the new glyph widths
862 if( pGlyphIter
+1 != pGlyphIterEnd
)
863 pGlyphIter
->m_aLinearPos
.AdjustX(nOffset
);
867 void GenericSalLayout::GetCaretPositions( int nMaxIndex
, tools::Long
* pCaretXArray
) const
869 // initialize result array
870 for (int i
= 0; i
< nMaxIndex
; ++i
)
871 pCaretXArray
[i
] = -1;
873 // calculate caret positions using glyph array
874 for (auto const& aGlyphItem
: m_GlyphItems
)
876 tools::Long nXPos
= aGlyphItem
.m_aLinearPos
.getX();
877 tools::Long nXRight
= nXPos
+ aGlyphItem
.origWidth();
878 int n
= aGlyphItem
.charPos();
879 int nCurrIdx
= 2 * (n
- mnMinCharPos
);
880 // tdf#86399 if this is not the start of a cluster, don't overwrite the caret bounds of the cluster start
881 if (aGlyphItem
.IsInCluster() && pCaretXArray
[nCurrIdx
] != -1)
883 if (!aGlyphItem
.IsRTLGlyph() )
885 // normal positions for LTR case
886 pCaretXArray
[ nCurrIdx
] = nXPos
;
887 pCaretXArray
[ nCurrIdx
+1 ] = nXRight
;
891 // reverse positions for RTL case
892 pCaretXArray
[ nCurrIdx
] = nXRight
;
893 pCaretXArray
[ nCurrIdx
+1 ] = nXPos
;
898 sal_Int32
GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth
, DeviceCoordinate nCharExtra
, int nFactor
) const
900 int nCharCapacity
= mnEndCharPos
- mnMinCharPos
;
901 std::unique_ptr
<DeviceCoordinate
[]> const pCharWidths(new DeviceCoordinate
[nCharCapacity
]);
902 GetCharWidths(pCharWidths
.get());
904 DeviceCoordinate nWidth
= 0;
905 for( int i
= mnMinCharPos
; i
< mnEndCharPos
; ++i
)
907 nWidth
+= pCharWidths
[ i
- mnMinCharPos
] * nFactor
;
908 if( nWidth
> nMaxWidth
)
910 nWidth
+= nCharExtra
;
916 bool GenericSalLayout::GetNextGlyph(const GlyphItem
** pGlyph
,
917 Point
& rPos
, int& nStart
,
918 const PhysicalFontFace
**) 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
->charPos();
928 if( (mnMinCharPos
<= n
) && (n
< mnEndCharPos
) )
932 // return zero if no more glyph found
933 if( nStart
>= static_cast<int>(m_GlyphItems
.size()) )
936 if( pGlyphIter
== pGlyphIterEnd
)
939 // update return data with glyph info
940 *pGlyph
= &(*pGlyphIter
);
943 // calculate absolute position in pixel units
944 Point aRelativePos
= pGlyphIter
->m_aLinearPos
;
946 aRelativePos
.setX( aRelativePos
.X() / mnUnitsPerPixel
);
947 aRelativePos
.setY( aRelativePos
.Y() / mnUnitsPerPixel
);
948 rPos
= GetDrawPosition( aRelativePos
);
953 void GenericSalLayout::MoveGlyph( int nStart
, tools::Long nNewXPos
)
955 if( nStart
>= static_cast<int>(m_GlyphItems
.size()) )
958 std::vector
<GlyphItem
>::iterator pGlyphIter
= m_GlyphItems
.begin();
959 pGlyphIter
+= nStart
;
961 // the nNewXPos argument determines the new cell position
962 // as RTL-glyphs are right justified in their cell
963 // the cell position needs to be adjusted to the glyph position
964 if( pGlyphIter
->IsRTLGlyph() )
965 nNewXPos
+= pGlyphIter
->m_nNewWidth
- pGlyphIter
->origWidth();
966 // calculate the x-offset to the old position
967 tools::Long nXDelta
= nNewXPos
- pGlyphIter
->m_aLinearPos
.getX() + pGlyphIter
->xOffset();
968 // adjust all following glyph positions if needed
971 for( std::vector
<GlyphItem
>::iterator pGlyphIterEnd
= m_GlyphItems
.end(); pGlyphIter
!= pGlyphIterEnd
; ++pGlyphIter
)
973 pGlyphIter
->m_aLinearPos
.AdjustX(nXDelta
);
978 void GenericSalLayout::DropGlyph( int nStart
)
980 if( nStart
>= static_cast<int>(m_GlyphItems
.size()))
983 std::vector
<GlyphItem
>::iterator pGlyphIter
= m_GlyphItems
.begin();
984 pGlyphIter
+= nStart
;
985 pGlyphIter
->dropGlyph();
988 void GenericSalLayout::Simplify( bool bIsBase
)
990 // remove dropped glyphs inplace
992 for(size_t i
= 0; i
< m_GlyphItems
.size(); i
++ )
994 if (bIsBase
&& m_GlyphItems
[i
].IsDropped())
996 if (!bIsBase
&& m_GlyphItems
[i
].glyphId() == 0)
1001 m_GlyphItems
[j
] = m_GlyphItems
[i
];
1005 m_GlyphItems
.erase(m_GlyphItems
.begin() + j
, m_GlyphItems
.end());
1008 MultiSalLayout::MultiSalLayout( std::unique_ptr
<SalLayout
> pBaseLayout
)
1011 , mbIncomplete( false )
1013 assert(dynamic_cast<GenericSalLayout
*>(pBaseLayout
.get()));
1015 mpLayouts
[ 0 ].reset(static_cast<GenericSalLayout
*>(pBaseLayout
.release()));
1016 mnUnitsPerPixel
= mpLayouts
[ 0 ]->GetUnitsPerPixel();
1019 std::unique_ptr
<SalLayout
> MultiSalLayout::ReleaseBaseLayout()
1021 return std::move(mpLayouts
[0]);
1024 void MultiSalLayout::SetIncomplete(bool bIncomplete
)
1026 mbIncomplete
= bIncomplete
;
1027 maFallbackRuns
[mnLevel
-1] = ImplLayoutRuns();
1030 MultiSalLayout::~MultiSalLayout()
1034 void MultiSalLayout::AddFallback( std::unique_ptr
<SalLayout
> pFallback
,
1035 ImplLayoutRuns
const & rFallbackRuns
)
1037 assert(dynamic_cast<GenericSalLayout
*>(pFallback
.get()));
1038 if( mnLevel
>= MAX_FALLBACK
)
1041 mpLayouts
[ mnLevel
].reset(static_cast<GenericSalLayout
*>(pFallback
.release()));
1042 maFallbackRuns
[ mnLevel
-1 ] = rFallbackRuns
;
1046 bool MultiSalLayout::LayoutText( ImplLayoutArgs
& rArgs
, const SalLayoutGlyphsImpl
* )
1051 maFallbackRuns
[ mnLevel
-1 ] = rArgs
.maRuns
;
1055 void MultiSalLayout::AdjustLayout( ImplLayoutArgs
& rArgs
)
1057 SalLayout::AdjustLayout( rArgs
);
1058 ImplLayoutArgs aMultiArgs
= rArgs
;
1059 std::unique_ptr
<DeviceCoordinate
[]> pJustificationArray
;
1061 if( !rArgs
.mpDXArray
&& rArgs
.mnLayoutWidth
)
1063 // for stretched text in a MultiSalLayout the target width needs to be
1064 // distributed by individually adjusting its virtual character widths
1065 DeviceCoordinate nTargetWidth
= aMultiArgs
.mnLayoutWidth
;
1066 nTargetWidth
*= mnUnitsPerPixel
; // convert target width to base font units
1067 aMultiArgs
.mnLayoutWidth
= 0;
1069 // we need to get the original unmodified layouts ready
1070 for( int n
= 0; n
< mnLevel
; ++n
)
1071 mpLayouts
[n
]->SalLayout::AdjustLayout( aMultiArgs
);
1072 // then we can measure the unmodified metrics
1073 int nCharCount
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
1074 pJustificationArray
.reset(new DeviceCoordinate
[nCharCount
]);
1075 FillDXArray( pJustificationArray
.get() );
1076 // #i17359# multilayout is not simplified yet, so calculating the
1077 // unjustified width needs handholding; also count the number of
1078 // stretchable virtual char widths
1079 DeviceCoordinate nOrigWidth
= 0;
1080 int nStretchable
= 0;
1081 for( int i
= 0; i
< nCharCount
; ++i
)
1083 // convert array from widths to sum of widths
1084 nOrigWidth
+= pJustificationArray
[i
];
1085 if( pJustificationArray
[i
] > 0 )
1089 // now we are able to distribute the extra width over the virtual char widths
1090 if( nOrigWidth
&& (nTargetWidth
!= nOrigWidth
) )
1092 DeviceCoordinate nDiffWidth
= nTargetWidth
- nOrigWidth
;
1093 DeviceCoordinate nWidthSum
= 0;
1094 for( int i
= 0; i
< nCharCount
; ++i
)
1096 DeviceCoordinate nJustWidth
= pJustificationArray
[i
];
1097 if( (nJustWidth
> 0) && (nStretchable
> 0) )
1099 DeviceCoordinate nDeltaWidth
= nDiffWidth
/ nStretchable
;
1100 nJustWidth
+= nDeltaWidth
;
1101 nDiffWidth
-= nDeltaWidth
;
1104 nWidthSum
+= nJustWidth
;
1105 pJustificationArray
[i
] = nWidthSum
;
1107 if( nWidthSum
!= nTargetWidth
)
1108 pJustificationArray
[ nCharCount
-1 ] = nTargetWidth
;
1110 // the justification array is still in base level units
1111 // => convert it to pixel units
1112 if( mnUnitsPerPixel
> 1 )
1114 for( int i
= 0; i
< nCharCount
; ++i
)
1116 DeviceCoordinate nVal
= pJustificationArray
[ i
];
1117 nVal
+= (mnUnitsPerPixel
+ 1) / 2;
1118 pJustificationArray
[ i
] = nVal
/ mnUnitsPerPixel
;
1122 // change the mpDXArray temporarily (just for the justification)
1123 aMultiArgs
.mpDXArray
= pJustificationArray
.get();
1127 // Compute rtl flags, since in some scripts glyphs/char order can be
1128 // reversed for a few character sequences e.g. Myanmar
1129 std::vector
<bool> vRtl(rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
, false);
1132 int nRunStart
, nRunEnd
;
1133 while (rArgs
.GetNextRun(&nRunStart
, &nRunEnd
, &bRtl
))
1135 if (bRtl
) std::fill(vRtl
.begin() + (nRunStart
- rArgs
.mnMinCharPos
),
1136 vRtl
.begin() + (nRunEnd
- rArgs
.mnMinCharPos
), true);
1140 // prepare "merge sort"
1141 int nStartOld
[ MAX_FALLBACK
];
1142 int nStartNew
[ MAX_FALLBACK
];
1143 const GlyphItem
* pGlyphs
[MAX_FALLBACK
];
1144 bool bValid
[MAX_FALLBACK
] = { false };
1148 for( n
= 0; n
< mnLevel
; ++n
)
1150 // now adjust the individual components
1153 aMultiArgs
.maRuns
= maFallbackRuns
[ n
-1 ];
1154 aMultiArgs
.mnFlags
|= SalLayoutFlags::ForFallback
;
1156 mpLayouts
[n
]->AdjustLayout( aMultiArgs
);
1158 // remove unused parts of component
1161 if (mbIncomplete
&& (n
== mnLevel
-1))
1162 mpLayouts
[n
]->Simplify( true );
1164 mpLayouts
[n
]->Simplify( false );
1167 // prepare merging components
1168 nStartNew
[ nLevel
] = nStartOld
[ nLevel
] = 0;
1169 bValid
[nLevel
] = mpLayouts
[n
]->GetNextGlyph(&pGlyphs
[nLevel
], aPos
, nStartNew
[nLevel
]);
1171 if( (n
> 0) && !bValid
[ nLevel
] )
1173 // an empty fallback layout can be released
1174 mpLayouts
[n
].reset();
1178 // reshuffle used fallbacks if needed
1181 mpLayouts
[ nLevel
] = std::move(mpLayouts
[ n
]);
1182 maFallbackRuns
[ nLevel
] = maFallbackRuns
[ n
];
1189 // prepare merge the fallback levels
1190 tools::Long nXPos
= 0;
1191 double fUnitMul
= 1.0;
1192 for( n
= 0; n
< nLevel
; ++n
)
1193 maFallbackRuns
[n
].ResetPos();
1195 int nFirstValid
= -1;
1196 for( n
= 0; n
< nLevel
; ++n
)
1204 assert(nFirstValid
>= 0);
1206 // get the next codepoint index that needs fallback
1207 int nActiveCharPos
= pGlyphs
[nFirstValid
]->charPos();
1208 int nActiveCharIndex
= nActiveCharPos
- mnMinCharPos
;
1209 // get the end index of the active run
1210 int nLastRunEndChar
= (nActiveCharIndex
>= 0 && vRtl
[nActiveCharIndex
]) ?
1211 rArgs
.mnEndCharPos
: rArgs
.mnMinCharPos
- 1;
1212 int nRunVisibleEndChar
= pGlyphs
[nFirstValid
]->charPos();
1213 // merge the fallback levels
1214 while( bValid
[nFirstValid
] && (nLevel
> 0))
1216 // find best fallback level
1217 for( n
= 0; n
< nLevel
; ++n
)
1218 if( bValid
[n
] && !maFallbackRuns
[n
].PosIsInAnyRun( nActiveCharPos
) )
1219 // fallback level n wins when it requested no further fallback
1225 // use base(n==0) or fallback(n>=1) level
1226 fUnitMul
= mnUnitsPerPixel
;
1227 fUnitMul
/= mpLayouts
[n
]->GetUnitsPerPixel();
1228 tools::Long nNewPos
= static_cast<tools::Long
>(nXPos
/fUnitMul
+ 0.5);
1229 mpLayouts
[n
]->MoveGlyph( nStartOld
[n
], nNewPos
);
1233 n
= 0; // keep NotDef in base level
1239 // drop the NotDef glyphs in the base layout run if a fallback run exists
1241 (maFallbackRuns
[n
-1].PosIsInRun(pGlyphs
[nFirstValid
]->charPos())) &&
1242 (!maFallbackRuns
[n
].PosIsInAnyRun(pGlyphs
[nFirstValid
]->charPos()))
1245 mpLayouts
[0]->DropGlyph( nStartOld
[0] );
1246 nStartOld
[0] = nStartNew
[0];
1247 bValid
[nFirstValid
] = mpLayouts
[0]->GetNextGlyph(&pGlyphs
[nFirstValid
], aPos
, nStartNew
[0]);
1249 if( !bValid
[nFirstValid
] )
1254 // skip to end of layout run and calculate its advance width
1255 DeviceCoordinate nRunAdvance
= 0;
1256 bool bKeepNotDef
= (nFBLevel
>= nLevel
);
1259 nRunAdvance
+= pGlyphs
[n
]->m_nNewWidth
;
1261 // proceed to next glyph
1262 nStartOld
[n
] = nStartNew
[n
];
1263 int nOrigCharPos
= pGlyphs
[n
]->charPos();
1264 bValid
[n
] = mpLayouts
[n
]->GetNextGlyph(&pGlyphs
[n
], aPos
, nStartNew
[n
]);
1265 // break after last glyph of active layout
1268 // performance optimization (when a fallback layout is no longer needed)
1274 //If the next character is one which belongs to the next level, then we
1275 //are finished here for now, and we'll pick up after the next level has
1277 if ((n
+1 < nLevel
) && (pGlyphs
[n
]->charPos() != nOrigCharPos
))
1279 if (nOrigCharPos
< pGlyphs
[n
]->charPos())
1281 if (pGlyphs
[n
+1]->charPos() > nOrigCharPos
&& (pGlyphs
[n
+1]->charPos() < pGlyphs
[n
]->charPos()))
1284 else if (nOrigCharPos
> pGlyphs
[n
]->charPos())
1286 if (pGlyphs
[n
+1]->charPos() > pGlyphs
[n
]->charPos() && (pGlyphs
[n
+1]->charPos() < nOrigCharPos
))
1291 // break at end of layout run
1294 // skip until end of fallback run
1295 if (!maFallbackRuns
[n
-1].PosIsInRun(pGlyphs
[n
]->charPos()))
1300 // break when a fallback is needed and available
1301 bool bNeedFallback
= maFallbackRuns
[0].PosIsInRun(pGlyphs
[nFirstValid
]->charPos());
1303 if (!maFallbackRuns
[nLevel
-1].PosIsInRun(pGlyphs
[nFirstValid
]->charPos()))
1305 // break when change from resolved to unresolved base layout run
1306 if( bKeepNotDef
&& !bNeedFallback
)
1307 { maFallbackRuns
[0].NextRun(); break; }
1308 bKeepNotDef
= bNeedFallback
;
1310 // check for reordered glyphs
1311 if (aMultiArgs
.mpDXArray
&&
1312 nRunVisibleEndChar
< mnEndCharPos
&&
1313 nRunVisibleEndChar
>= mnMinCharPos
&&
1314 pGlyphs
[n
]->charPos() < mnEndCharPos
&&
1315 pGlyphs
[n
]->charPos() >= mnMinCharPos
)
1317 if (vRtl
[nActiveCharPos
- mnMinCharPos
])
1319 if (aMultiArgs
.mpDXArray
[nRunVisibleEndChar
-mnMinCharPos
]
1320 >= aMultiArgs
.mpDXArray
[pGlyphs
[n
]->charPos() - mnMinCharPos
])
1322 nRunVisibleEndChar
= pGlyphs
[n
]->charPos();
1325 else if (aMultiArgs
.mpDXArray
[nRunVisibleEndChar
-mnMinCharPos
]
1326 <= aMultiArgs
.mpDXArray
[pGlyphs
[n
]->charPos() - mnMinCharPos
])
1328 nRunVisibleEndChar
= pGlyphs
[n
]->charPos();
1333 // if a justification array is available
1334 // => use it directly to calculate the corresponding run width
1335 if( aMultiArgs
.mpDXArray
)
1337 // the run advance is the width from the first char
1338 // in the run to the first char in the next run
1340 nActiveCharIndex
= nActiveCharPos
- mnMinCharPos
;
1341 if (nActiveCharIndex
>= 0 && vRtl
[nActiveCharIndex
])
1343 if (nRunVisibleEndChar
> mnMinCharPos
&& nRunVisibleEndChar
<= mnEndCharPos
)
1344 nRunAdvance
-= aMultiArgs
.mpDXArray
[nRunVisibleEndChar
- 1 - mnMinCharPos
];
1345 if (nLastRunEndChar
> mnMinCharPos
&& nLastRunEndChar
<= mnEndCharPos
)
1346 nRunAdvance
+= aMultiArgs
.mpDXArray
[nLastRunEndChar
- 1 - mnMinCharPos
];
1350 if (nRunVisibleEndChar
>= mnMinCharPos
)
1351 nRunAdvance
+= aMultiArgs
.mpDXArray
[nRunVisibleEndChar
- mnMinCharPos
];
1352 if (nLastRunEndChar
>= mnMinCharPos
)
1353 nRunAdvance
-= aMultiArgs
.mpDXArray
[nLastRunEndChar
- mnMinCharPos
];
1355 nLastRunEndChar
= nRunVisibleEndChar
;
1356 nRunVisibleEndChar
= pGlyphs
[nFirstValid
]->charPos();
1357 // the requested width is still in pixel units
1358 // => convert it to base level font units
1359 nRunAdvance
*= mnUnitsPerPixel
;
1363 // the measured width is still in fallback font units
1364 // => convert it to base level font units
1365 if( n
> 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1366 nRunAdvance
= static_cast<tools::Long
>(nRunAdvance
*fUnitMul
+ 0.5);
1369 // calculate new x position (in base level units)
1370 nXPos
+= nRunAdvance
;
1372 // prepare for next fallback run
1373 nActiveCharPos
= pGlyphs
[nFirstValid
]->charPos();
1374 // it essential that the runs don't get ahead of themselves and in the
1375 // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1376 // have already been reached on the base level
1377 for( int i
= nFBLevel
; --i
>= 0;)
1379 if (maFallbackRuns
[i
].GetRun(&nRunStart
, &nRunEnd
, &bRtl
))
1383 if (nRunStart
> nActiveCharPos
)
1384 maFallbackRuns
[i
].NextRun();
1388 if (nRunEnd
<= nActiveCharPos
)
1389 maFallbackRuns
[i
].NextRun();
1395 mpLayouts
[0]->Simplify( true );
1398 void MultiSalLayout::InitFont() const
1401 mpLayouts
[0]->InitFont();
1404 void MultiSalLayout::DrawText( SalGraphics
& rGraphics
) const
1406 for( int i
= mnLevel
; --i
>= 0; )
1408 SalLayout
& rLayout
= *mpLayouts
[ i
];
1409 rLayout
.DrawBase() += maDrawBase
;
1410 rLayout
.DrawOffset() += maDrawOffset
;
1412 rLayout
.DrawText( rGraphics
);
1413 rLayout
.DrawOffset() -= maDrawOffset
;
1414 rLayout
.DrawBase() -= maDrawBase
;
1416 // NOTE: now the baselevel font is active again
1419 sal_Int32
MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth
, DeviceCoordinate nCharExtra
, int nFactor
) const
1424 return mpLayouts
[0]->GetTextBreak( nMaxWidth
, nCharExtra
, nFactor
);
1426 int nCharCount
= mnEndCharPos
- mnMinCharPos
;
1427 std::unique_ptr
<DeviceCoordinate
[]> const pCharWidths(new DeviceCoordinate
[nCharCount
]);
1428 std::unique_ptr
<DeviceCoordinate
[]> const pFallbackCharWidths(new DeviceCoordinate
[nCharCount
]);
1429 mpLayouts
[0]->FillDXArray( pCharWidths
.get() );
1431 for( int n
= 1; n
< mnLevel
; ++n
)
1433 SalLayout
& rLayout
= *mpLayouts
[ n
];
1434 rLayout
.FillDXArray( pFallbackCharWidths
.get() );
1435 double fUnitMul
= mnUnitsPerPixel
;
1436 fUnitMul
/= rLayout
.GetUnitsPerPixel();
1437 for( int i
= 0; i
< nCharCount
; ++i
)
1439 if( pCharWidths
[ i
] == 0 )
1441 DeviceCoordinate w
= pFallbackCharWidths
[i
];
1442 w
= static_cast<DeviceCoordinate
>(w
* fUnitMul
+ 0.5);
1443 pCharWidths
[ i
] = w
;
1448 DeviceCoordinate nWidth
= 0;
1449 for( int i
= 0; i
< nCharCount
; ++i
)
1451 nWidth
+= pCharWidths
[ i
] * nFactor
;
1452 if( nWidth
> nMaxWidth
)
1453 return (i
+ mnMinCharPos
);
1454 nWidth
+= nCharExtra
;
1460 DeviceCoordinate
MultiSalLayout::FillDXArray( DeviceCoordinate
* pCharWidths
) const
1462 DeviceCoordinate nMaxWidth
= 0;
1464 // prepare merging of fallback levels
1465 std::unique_ptr
<DeviceCoordinate
[]> pTempWidths
;
1466 const int nCharCount
= mnEndCharPos
- mnMinCharPos
;
1469 for( int i
= 0; i
< nCharCount
; ++i
)
1471 pTempWidths
.reset(new DeviceCoordinate
[nCharCount
]);
1474 for( int n
= mnLevel
; --n
>= 0; )
1476 // query every fallback level
1477 DeviceCoordinate nTextWidth
= mpLayouts
[n
]->FillDXArray( pTempWidths
.get() );
1480 // merge results from current level
1481 double fUnitMul
= mnUnitsPerPixel
;
1482 fUnitMul
/= mpLayouts
[n
]->GetUnitsPerPixel();
1483 nTextWidth
= static_cast<DeviceCoordinate
>(nTextWidth
* fUnitMul
+ 0.5);
1484 if( nMaxWidth
< nTextWidth
)
1485 nMaxWidth
= nTextWidth
;
1488 // calculate virtual char widths using most probable fallback layout
1489 for( int i
= 0; i
< nCharCount
; ++i
)
1491 // #i17359# restriction:
1492 // one char cannot be resolved from different fallbacks
1493 if( pCharWidths
[i
] != 0 )
1495 DeviceCoordinate nCharWidth
= pTempWidths
[i
];
1498 nCharWidth
= static_cast<DeviceCoordinate
>(nCharWidth
* fUnitMul
+ 0.5);
1499 pCharWidths
[i
] = nCharWidth
;
1506 void MultiSalLayout::GetCaretPositions( int nMaxIndex
, tools::Long
* pCaretXArray
) const
1508 SalLayout
& rLayout
= *mpLayouts
[ 0 ];
1509 rLayout
.GetCaretPositions( nMaxIndex
, pCaretXArray
);
1514 std::unique_ptr
<tools::Long
[]> const pTempPos(new tools::Long
[nMaxIndex
]);
1515 for( int n
= 1; n
< mnLevel
; ++n
)
1517 mpLayouts
[ n
]->GetCaretPositions( nMaxIndex
, pTempPos
.get() );
1518 double fUnitMul
= mnUnitsPerPixel
;
1519 fUnitMul
/= mpLayouts
[n
]->GetUnitsPerPixel();
1520 for( int i
= 0; i
< nMaxIndex
; ++i
)
1521 if( pTempPos
[i
] >= 0 )
1523 tools::Long w
= pTempPos
[i
];
1524 w
= static_cast<tools::Long
>(w
*fUnitMul
+ 0.5);
1525 pCaretXArray
[i
] = w
;
1530 bool MultiSalLayout::GetNextGlyph(const GlyphItem
** pGlyph
,
1531 Point
& rPos
, int& nStart
,
1532 const PhysicalFontFace
** pFallbackFont
) const
1534 // NOTE: nStart is tagged with current font index
1535 int nLevel
= static_cast<unsigned>(nStart
) >> GF_FONTSHIFT
;
1536 nStart
&= ~GF_FONTMASK
;
1537 for(; nLevel
< mnLevel
; ++nLevel
, nStart
=0 )
1539 GenericSalLayout
& rLayout
= *mpLayouts
[ nLevel
];
1541 const PhysicalFontFace
* pFontFace
= rLayout
.GetFont().GetFontFace();
1542 if (rLayout
.GetNextGlyph(pGlyph
, rPos
, nStart
))
1544 int nFontTag
= nLevel
<< GF_FONTSHIFT
;
1547 *pFallbackFont
= pFontFace
;
1549 rPos
+= maDrawOffset
;
1554 // #111016# reset to base level font when done
1555 mpLayouts
[0]->InitFont();
1559 bool MultiSalLayout::GetOutline(basegfx::B2DPolyPolygonVector
& rPPV
) const
1563 for( int i
= mnLevel
; --i
>= 0; )
1565 SalLayout
& rLayout
= *mpLayouts
[ i
];
1566 rLayout
.DrawBase() = maDrawBase
;
1567 rLayout
.DrawOffset() += maDrawOffset
;
1569 bRet
|= rLayout
.GetOutline(rPPV
);
1570 rLayout
.DrawOffset() -= maDrawOffset
;
1576 bool MultiSalLayout::IsKashidaPosValid(int nCharPos
) const
1578 // Check the base layout
1579 bool bValid
= mpLayouts
[0]->IsKashidaPosValid(nCharPos
);
1581 // If base layout returned false, it might be because the character was not
1582 // supported there, so we check fallback layouts.
1585 for (int i
= 1; i
< mnLevel
; ++i
)
1587 // - 1 because there is no fallback run for the base layout, IIUC.
1588 if (maFallbackRuns
[i
- 1].PosIsInAnyRun(nCharPos
))
1590 bValid
= mpLayouts
[i
]->IsKashidaPosValid(nCharPos
);
1599 SalLayoutGlyphs
MultiSalLayout::GetGlyphs() const
1601 SalLayoutGlyphs glyphs
;
1602 for( int n
= 0; n
< mnLevel
; ++n
)
1603 glyphs
.AppendImpl(mpLayouts
[n
]->GlyphsImpl().clone());
1608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */