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"
28 #include <sal/alloca.h>
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>
44 #pragma warning(push, 1)
46 #include <unicode/ubidi.h>
47 #include <unicode/uchar.h>
53 #include <boost/scoped_array.hpp>
56 //#define MULTI_SL_DEBUG
61 FILE * mslLogFile
= NULL
;
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
);
76 std::ostream
&operator <<(std::ostream
& s
, ImplLayoutArgs
&rArgs
)
81 s
<< "ImplLayoutArgs{";
84 if (rArgs
.mnFlags
== SalLayoutFlags::NONE
)
89 #define TEST(x) if (rArgs.mnFlags & SalLayoutFlags::x) { if (need_or) s << "|"; s << #x; need_or = true; }
96 TEST(ComplexDisabled
);
97 TEST(EnableLigatures
);
98 TEST(SubstituteDigits
);
99 TEST(KashidaJustification
);
100 TEST(DisableGlyphProcessing
);
106 s
<< ",Length=" << rArgs
.mnLength
;
107 s
<< ",MinCharPos=" << rArgs
.mnMinCharPos
;
108 s
<< ",EndCharPos=" << rArgs
.mnEndCharPos
;
111 int lim
= rArgs
.mnLength
;
114 for (int i
= 0; i
< lim
; i
++) {
115 if (rArgs
.mpStr
[i
] == '\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
];
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
)
129 if (rArgs
.mpDXArray
) {
131 int count
= rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
;
135 for (int i
= 0; i
< lim
; i
++) {
136 s
<< rArgs
.mpDXArray
[i
];
143 s
<< rArgs
.mpDXArray
[count
-1];
149 s
<< ",LayoutWidth=" << rArgs
.mnLayoutWidth
;
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
164 if( nChar
>= 0x2100 )
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
[] = {
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},
175 {0x205F, 0x2070}, {0x20D0, 0x2100},
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
) )
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
);
223 sal_UCS4
GetLocalizedChar( sal_UCS4 nChar
, LanguageType eLang
)
225 // currently only conversion from ASCII digits is interesting
226 if( (nChar
< '0') || ('9' < nChar
) )
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
)
238 case LANGUAGE_ARABIC_SAUDI_ARABIA
& LANGUAGE_MASK_PRIMARY
:
239 nOffset
= 0x0660 - '0'; // arabic-indic digits
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
247 case LANGUAGE_BENGALI
& LANGUAGE_MASK_PRIMARY
:
248 nOffset
= 0x09E6 - '0'; // bengali
250 case LANGUAGE_HINDI
& LANGUAGE_MASK_PRIMARY
:
251 nOffset
= 0x0966 - '0'; // devanagari
253 case LANGUAGE_AMHARIC_ETHIOPIA
& LANGUAGE_MASK_PRIMARY
:
254 case LANGUAGE_TIGRIGNA_ETHIOPIA
& LANGUAGE_MASK_PRIMARY
:
256 nOffset
= 0x1369 - '0'; // ethiopic
258 case LANGUAGE_GUJARATI
& LANGUAGE_MASK_PRIMARY
:
259 nOffset
= 0x0AE6 - '0'; // gujarati
261 #ifdef LANGUAGE_GURMUKHI // TODO case:
262 case LANGUAGE_GURMUKHI
& LANGUAGE_MASK_PRIMARY
:
263 nOffset
= 0x0A66 - '0'; // gurmukhi
266 case LANGUAGE_KANNADA
& LANGUAGE_MASK_PRIMARY
:
267 nOffset
= 0x0CE6 - '0'; // kannada
269 case LANGUAGE_KHMER
& LANGUAGE_MASK_PRIMARY
:
270 nOffset
= 0x17E0 - '0'; // khmer
272 case LANGUAGE_LAO
& LANGUAGE_MASK_PRIMARY
:
273 nOffset
= 0x0ED0 - '0'; // lao
275 case LANGUAGE_MALAYALAM
& LANGUAGE_MASK_PRIMARY
:
276 nOffset
= 0x0D66 - '0'; // malayalam
278 case LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
& LANGUAGE_MASK_PRIMARY
:
281 case LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
:
282 case LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
:
283 case LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
:
284 nOffset
= 0x1810 - '0'; // mongolian
287 nOffset
= 0; // mongolian cyrillic
291 case LANGUAGE_BURMESE
& LANGUAGE_MASK_PRIMARY
:
292 nOffset
= 0x1040 - '0'; // myanmar
294 case LANGUAGE_ODIA
& LANGUAGE_MASK_PRIMARY
:
295 nOffset
= 0x0B66 - '0'; // odia
297 case LANGUAGE_TAMIL
& LANGUAGE_MASK_PRIMARY
:
298 nOffset
= 0x0BE7 - '0'; // tamil
300 case LANGUAGE_TELUGU
& LANGUAGE_MASK_PRIMARY
:
301 nOffset
= 0x0C66 - '0'; // telugu
303 case LANGUAGE_THAI
& LANGUAGE_MASK_PRIMARY
:
304 nOffset
= 0x0E50 - '0'; // thai
306 case LANGUAGE_TIBETAN
& LANGUAGE_MASK_PRIMARY
:
307 nOffset
= 0x0F20 - '0'; // tibetan
315 inline bool IsControlChar( sal_UCS4 cChar
)
317 // C0 control characters
318 if( (0x0001 <= cChar
) && (cChar
<= 0x001F) )
320 // formatting characters
321 if( (0x200E <= cChar
) && (cChar
<= 0x200F) )
323 if( (0x2028 <= cChar
) && (cChar
<= 0x202E) )
325 // deprecated formatting characters
326 if( (0x206A <= cChar
) && (cChar
<= 0x206F) )
328 if( (0x2060 == cChar
) )
330 // byte order markers and invalid unicode
331 if( (cChar
== 0xFEFF) || (cChar
== 0xFFFE) || (cChar
== 0xFFFF) )
336 bool ImplLayoutRuns::AddPos( int nCharPos
, bool bRTL
)
338 // check if charpos could extend current run
339 int nIndex
= maRuns
.size();
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
);
350 // ignore new charpos when it is in current run
351 if( (nRunPos0
<= nCharPos
) && (nCharPos
< nRunPos1
) )
353 if( (nRunPos1
<= nCharPos
) && (nCharPos
< nRunPos0
) )
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) );
363 bool ImplLayoutRuns::AddRun( int nCharPos0
, int nCharPos1
, bool bRTL
)
365 if( nCharPos0
== nCharPos1
)
369 if( bRTL
== (nCharPos0
< nCharPos1
) )
371 int nTemp
= nCharPos0
;
372 nCharPos0
= nCharPos1
;
377 maRuns
.push_back( nCharPos0
);
378 maRuns
.push_back( nCharPos1
);
382 bool ImplLayoutRuns::PosIsInRun( int nCharPos
) const
384 if( mnRunIndex
>= (int)maRuns
.size() )
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
;
396 if( nCharPos
< nMinCharPos
)
398 if( nCharPos
>= nEndCharPos
)
403 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos
) const
406 int nRunIndex
= mnRunIndex
;
408 ImplLayoutRuns
*pThis
= const_cast<ImplLayoutRuns
*>(this);
412 for (size_t i
= 0; i
< maRuns
.size(); i
+=2)
414 if( (bRet
= PosIsInRun( nCharPos
)) )
419 pThis
->mnRunIndex
= nRunIndex
;
423 bool ImplLayoutRuns::GetNextPos( int* nCharPos
, bool* bRightToLeft
)
425 // negative nCharPos => reset to first run
429 // return false when all runs completed
430 if( mnRunIndex
>= (int)maRuns
.size() )
433 int nRunPos0
= maRuns
[ mnRunIndex
+0 ];
434 int nRunPos1
= maRuns
[ mnRunIndex
+1 ];
435 *bRightToLeft
= (nRunPos0
> nRunPos1
);
439 // get first valid nCharPos in run
440 *nCharPos
= nRunPos0
;
444 // advance to next nCharPos for LTR case
448 // advance to next run if current run is completed
449 if( *nCharPos
== nRunPos1
)
451 if( (mnRunIndex
+= 2) >= (int)maRuns
.size() )
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
467 bool ImplLayoutRuns::GetRun( int* nMinRunPos
, int* nEndRunPos
, bool* bRightToLeft
) const
469 if( mnRunIndex
>= (int)maRuns
.size() )
472 int nRunPos0
= maRuns
[ mnRunIndex
+0 ];
473 int nRunPos1
= maRuns
[ mnRunIndex
+1 ];
474 *bRightToLeft
= (nRunPos1
< nRunPos0
) ;
477 *nMinRunPos
= nRunPos0
;
478 *nEndRunPos
= nRunPos1
;
482 *nMinRunPos
= nRunPos1
;
483 *nEndRunPos
= nRunPos0
;
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
),
495 mnMinCharPos( nMinCharPos
),
496 mnEndCharPos( nEndCharPos
),
498 m_pTextLayoutCache(pLayoutCache
),
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
);
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
);
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
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
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
);
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
);
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() )
601 // convert the fallback requests to layout requests
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
;
621 for(; maRuns
.GetRun( &nMin
, &nEnd
, &bRTL
); maRuns
.NextRun() )
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
);
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()
639 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos
, int* nEndRunPos
, bool* bRTL
)
641 bool bValid
= maRuns
.GetRun( nMinRunPos
, nEndRunPos
, bRTL
);
646 SalLayout::SalLayout()
647 : mnMinCharPos( -1 ),
649 mnLayoutFlags( SalLayoutFlags::NONE
),
650 mnUnitsPerPixel( 1 ),
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 )
672 // const_cast because some compilers violate ANSI C++ spec
676 Point
SalLayout::GetDrawPosition( const Point
& rRelative
) const
678 Point aPos
= maDrawBase
;
679 Point aOfs
= rRelative
+ maDrawOffset
;
681 if( mnOrientation
== 0 )
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);
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
);
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
724 if( (c
>= 0x3000) && (c
< 0x3030) )
725 nResult
= nTable
[ c
- 0x3000 ];
729 nResult
= bLeft
? -1 : +1; // 25% left/right/top/bottom
731 case 0x2019: case 0x201D:
732 case 0xFF01: case 0xFF09: case 0xFF0C:
733 case 0xFF1A: case 0xFF1B:
736 case 0x2018: case 0x201C:
747 bool SalLayout::GetOutline( SalGraphics
& rSalGraphics
,
748 ::basegfx::B2DPolyPolygonVector
& rVector
) const
754 ::basegfx::B2DPolyPolygon aGlyphOutline
;
755 for( int nStart
= 0;;)
758 if( !GetNextGlyphs( 1, &nLGlyph
, aPos
, nStart
) )
761 // get outline of individual glyph, ignoring "empty" glyphs
762 bool bSuccess
= rSalGraphics
.GetGlyphOutline( nLGlyph
, aGlyphOutline
);
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
787 Rectangle aRectangle
;
788 for( int nStart
= 0;;)
791 if( !GetNextGlyphs( 1, &nLGlyph
, aPos
, nStart
) )
794 // get bounding rectangle of individual glyph
795 if( rSalGraphics
.GetGlyphBoundRect( nLGlyph
, aRectangle
) )
802 rRect
.Union(aRectangle
);
810 bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph
)
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
822 bRet
= ((nGlyph
& GF_IDXMASK
) == 3);
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
)
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() )
851 int n
= pGlyphIter
->mnCharPos
;
852 if( n
>= mnEndCharPos
)
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
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
)
877 // get rightmost x-extent of this glyph
878 nXPos
+= pGlyphIter
->mnNewWidth
;
879 if( 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() )
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
911 DeviceCoordinate
GenericSalLayout::FillDXArray( DeviceCoordinate
* pCharWidths
) const
914 if( !GetCharWidths( pCharWidths
) )
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() )
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
)
936 nXPos
+= pGlyphIter
->mnNewWidth
- pGlyphIter
->mnXOffset
;
937 if( nMaxPos
< nXPos
)
941 DeviceCoordinate nWidth
= nMaxPos
- nMinPos
;
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
)
965 nXPos
+= pGlyphIter
->mnNewWidth
- pGlyphIter
->mnXOffset
;
966 if( nMaxXPos
< nXPos
)
969 if( nMinYPos
> nYPos
)
971 nYPos
+= pGlyphIter
->mnNewWidth
- pGlyphIter
->mnYOffset
;
972 if( 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
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
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
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())
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) ));
1038 long nBasePointX
= -1;
1039 if( mnLayoutFlags
& SalLayoutFlags::ForFallback
)
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
) )
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 )
1059 if( n
>= nCharCount
)
1061 for( n
= 0; n
< nCharCount
; ++n
)
1063 if( pLogCluster
[ n
] < 0 )
1064 pLogCluster
[ n
] = p
;
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;
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
];
1084 long nDelta
= rArgs
.mpDXArray
[ n
] ;
1086 nDelta
-= rArgs
.mpDXArray
[ n
-1 ];
1087 pNewGlyphWidths
[ p
] += nDelta
* mnUnitsPerPixel
;
1091 // move cluster positions using the adjusted widths
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
];
1102 for( j
= i
; ++j
< m_GlyphItems
.size(); )
1104 if( m_GlyphItems
[j
].IsClusterStart() )
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
;
1121 // right align cluster in new space for RTL case
1122 m_GlyphItems
[i
].mnNewWidth
+= 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
)
1140 if(m_GlyphItems
.empty())
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() )
1155 if( nMaxGlyphWidth
< pGlyphIter
->mnOrigWidth
)
1156 nMaxGlyphWidth
= pGlyphIter
->mnOrigWidth
;
1159 // move rightmost glyph to requested position
1160 nOldWidth
-= pGlyphIterRight
->mnOrigWidth
;
1161 if( nOldWidth
<= 0 )
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
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) )
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
)
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)) )
1222 const sal_Unicode cNext
= pStr
[n
+1];
1223 if( ((0x3000 != (cNext
& 0xFF00)) && (0x2010 != (cNext
& 0xFFF0))) || (0xFF00 != (cNext
& 0xFF00)) )
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
;
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 )
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() )
1265 // no kashida-injection for blank justified expansion either
1266 if( IsSpacingGlyph( pGlyphIter
->maGlyphId
) )
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
)
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
));
1285 aPos
.X() += nKashidaWidth
;
1288 // fixup rightmost kashida for gap remainder
1291 pGlyphIter2
= m_GlyphItems
.insert(pGlyphIter2
, GlyphItem(nCharPos
, nKashidaIndex
, aPos
,
1292 GlyphItem::IS_IN_CLUSTER
|GlyphItem::IS_RTL_GLYPH
, nKashidaCount
? nGapWidth
: nGapWidth
/2 ));
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)
1316 if( !pGlyphIter
->IsRTLGlyph() )
1318 // normal positions for LTR case
1319 pCaretXArray
[ nCurrIdx
] = nXPos
;
1320 pCaretXArray
[ nCurrIdx
+1 ] = nXRight
;
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
) )
1338 DeviceCoordinate nWidth
= 0;
1339 for( int i
= mnMinCharPos
; i
< mnEndCharPos
; ++i
)
1341 nWidth
+= pCharWidths
[ i
- mnMinCharPos
] * nFactor
;
1342 if( nWidth
> nMaxWidth
)
1344 nWidth
+= nCharExtra
;
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
) )
1366 // return zero if no more glyph found
1367 if( nStart
>= (int)m_GlyphItems
.size() )
1370 if( pGlyphIter
== pGlyphIterEnd
)
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
1378 long nYPos
= pGlyphIter
->maLinearPos
.Y();
1379 long nOldFlags
= pGlyphIter
->maGlyphId
;
1382 // update return data with glyph info
1384 *(pGlyphs
++) = pGlyphIter
->maGlyphId
;
1386 *(pCharPosAry
++) = pGlyphIter
->mnCharPos
;
1388 *pGlyphAdvAry
= pGlyphIter
->mnNewWidth
;
1390 // break at end of glyph list
1391 if( ++nStart
>= (int)m_GlyphItems
.size() )
1393 // break when enough glyphs
1394 if( nCount
>= nLen
)
1397 long nGlyphAdvance
= pGlyphIter
[1].maLinearPos
.X() - pGlyphIter
->maLinearPos
.X();
1400 // override default advance width with correct value
1401 *(pGlyphAdvAry
++) = nGlyphAdvance
;
1405 // stop when next x-position is unexpected
1406 if( pGlyphIter
->mnOrigWidth
!= nGlyphAdvance
)
1410 // advance to next glyph
1413 // stop when next y-position is unexpected
1414 if( nYPos
!= pGlyphIter
->maLinearPos
.Y() )
1417 // stop when no longer in string
1418 int n
= pGlyphIter
->mnCharPos
;
1419 if( (n
< mnMinCharPos
) || (mnEndCharPos
<= n
) )
1422 // stop when glyph flags change
1423 if( (nOldFlags
^ pGlyphIter
->maGlyphId
) & GF_FLAGMASK
)
1426 nOldFlags
= pGlyphIter
->maGlyphId
; // &GF_FLAGMASK not needed for test above
1429 aRelativePos
.X() /= mnUnitsPerPixel
;
1430 aRelativePos
.Y() /= mnUnitsPerPixel
;
1431 rPos
= GetDrawPosition( aRelativePos
);
1436 void GenericSalLayout::MoveGlyph( int nStart
, long nNewXPos
)
1438 if( nStart
>= (int)m_GlyphItems
.size() )
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
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())
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
1478 for(size_t i
= 0; i
< m_GlyphItems
.size(); i
++ )
1480 if( m_GlyphItems
[i
].maGlyphId
== nDropMarker
)
1485 m_GlyphItems
[j
] = m_GlyphItems
[i
];
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() )
1503 if( !pGlyphIter
->IsClusterStart() )
1505 for( GlyphVector::iterator pBaseGlyph
= pGlyphIter
; ++pBaseGlyph
!= pGlyphIterEnd
; )
1507 // find the base glyph matching to the misplaced diacritic
1508 if( pBaseGlyph
->IsClusterStart() )
1510 if( pBaseGlyph
->IsDiacritic() )
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
;
1527 MultiSalLayout::MultiSalLayout( SalLayout
& rBaseLayout
, const PhysicalFontFace
* pBaseFont
)
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
)
1556 mpFallbackFonts
[ mnLevel
] = pFallbackFont
;
1557 mpLayouts
[ mnLevel
] = &rFallback
;
1558 maFallbackRuns
[ mnLevel
-1 ] = rFallbackRuns
;
1563 bool MultiSalLayout::LayoutText( ImplLayoutArgs
& rArgs
)
1568 maFallbackRuns
[ mnLevel
-1 ] = rArgs
.maRuns
;
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 )
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
;
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);
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);
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};
1667 for( n
= 0; n
< mnLevel
; ++n
)
1669 // now adjust the individual components
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
1683 if (mbInComplete
&& (n
== mnLevel
-1))
1684 mpLayouts
[n
]->Simplify( true );
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
]]);
1697 if( (n
> 0) && !nValid
[ nLevel
] )
1699 // an empty fallback layout can be released
1700 mpLayouts
[n
]->Release();
1704 // reshuffle used fallbacks if needed
1707 mpLayouts
[ nLevel
] = mpLayouts
[ n
];
1708 mpFallbackFonts
[ nLevel
] = mpFallbackFonts
[ n
];
1709 maFallbackRuns
[ nLevel
] = maFallbackRuns
[ n
];
1716 // prepare merge the fallback levels
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
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
);
1748 n
= 0; // keep NotDef in base level
1754 // drop the NotDef glyphs in the base layout run if a fallback run exists
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]]);
1772 // skip to end of layout run and calculate its advance width
1773 DeviceCoordinate nRunAdvance
= 0;
1774 bool bKeepNotDef
= (nFBLevel
>= nLevel
);
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
]]);
1787 // break after last glyph of active layout
1790 // performance optimization (when a fallback layout is no longer needed)
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
1799 if ((n
+1 < nLevel
) && (nCharPos
[n
] != nOrigCharPos
))
1801 if (nOrigCharPos
< nCharPos
[n
])
1803 if (nCharPos
[n
+1] > nOrigCharPos
&& (nCharPos
[n
+1] < nCharPos
[n
]))
1806 else if (nOrigCharPos
> nCharPos
[n
])
1808 if (nCharPos
[n
+1] > nCharPos
[n
] && (nCharPos
[n
+1] < nOrigCharPos
))
1813 // break at end of layout run
1816 // skip until end of fallback run
1817 if( !maFallbackRuns
[n
-1].PosIsInRun( nCharPos
[n
] ) )
1822 // break when a fallback is needed and available
1823 bool bNeedFallback
= maFallbackRuns
[0].PosIsInRun( nCharPos
[0] );
1825 if( !maFallbackRuns
[ nLevel
-1 ].PosIsInRun( nCharPos
[0] ) )
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
1862 #ifdef MULTI_SL_DEBUG
1863 const bool bLTR
= !(vRtl
[nActiveCharPos
- mnMinCharPos
]);//(nActiveCharPos < nCharPos[0]);
1865 int nDXIndex
= nCharPos
[0] - mnMinCharPos
- bLTR
;
1867 nOldRunAdv
+= aMultiArgs
.mpDXArray
[ nDXIndex
];
1868 nDXIndex
= nActiveCharPos
- mnMinCharPos
- bLTR
;
1870 nOldRunAdv
-= aMultiArgs
.mpDXArray
[ nDXIndex
];
1872 nOldRunAdv
= -nOldRunAdv
;
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
);
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
);
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
;
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
))
1923 if (nRunStart
> nActiveCharPos
)
1924 maFallbackRuns
[i
].NextRun();
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
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
;
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
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
;
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
;
2012 for( int i
= 0; i
< nCharCount
; ++i
)
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
);
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
;
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 )
2038 DeviceCoordinate nCharWidth
= pTempWidths
[i
];
2041 nCharWidth
= (DeviceCoordinate
)(nCharWidth
* fUnitMul
+ 0.5);
2042 pCharWidths
[i
] = nCharWidth
;
2049 void MultiSalLayout::GetCaretPositions( int nMaxIndex
, long* pCaretXArray
) const
2051 SalLayout
& rLayout
= *mpLayouts
[ 0 ];
2052 rLayout
.GetCaretPositions( nMaxIndex
, pCaretXArray
);
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 )
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
];
2088 int nRetVal
= rLayout
.GetNextGlyphs( nLen
, pGlyphIdxAry
, rPos
,
2089 nStart
, pGlyphAdvAry
, pCharPosAry
);
2092 int nFontTag
= nLevel
<< GF_FONTSHIFT
;
2094 double fUnitMul
= mnUnitsPerPixel
;
2095 fUnitMul
/= mpLayouts
[nLevel
]->GetUnitsPerPixel();
2096 for( int i
= 0; i
< nRetVal
; ++i
)
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
];
2111 rPos
+= maDrawOffset
;
2116 // #111016# reset to base level font when done
2117 mpLayouts
[0]->InitFont();
2121 bool MultiSalLayout::GetOutline( SalGraphics
& rGraphics
,
2122 ::basegfx::B2DPolyPolygonVector
& rPPV
) const
2126 for( int i
= mnLevel
; --i
>= 0; )
2128 SalLayout
& rLayout
= *mpLayouts
[ i
];
2129 rLayout
.DrawBase() = maDrawBase
;
2130 rLayout
.DrawOffset() += maDrawOffset
;
2132 bRet
|= rLayout
.GetOutline( rGraphics
, rPPV
);
2133 rLayout
.DrawOffset() -= maDrawOffset
;
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: */