summaryrefslogtreecommitdiff
path: root/vcl/source/gdi/sallayout.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/gdi/sallayout.cxx')
-rwxr-xr-xvcl/source/gdi/sallayout.cxx2326
1 files changed, 2326 insertions, 0 deletions
diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx
new file mode 100755
index 000000000000..5e187944c706
--- /dev/null
+++ b/vcl/source/gdi/sallayout.cxx
@@ -0,0 +1,2326 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <cstdio>
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <sal/alloca.h>
+
+#ifndef _SV_SVSYS_HXX
+#include <svsys.h>
+#endif
+#include <vcl/salgdi.hxx>
+#include <vcl/sallayout.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <i18npool/lang.h>
+
+#ifndef _TL_DEBUG_HXX
+#include <tools/debug.hxx>
+#endif
+
+#include <limits.h>
+
+#if defined _MSC_VER
+#pragma warning(push, 1)
+#endif
+#include <unicode/ubidi.h>
+#include <unicode/uchar.h>
+#if defined _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include <algorithm>
+
+#ifdef DEBUG
+//#define MULTI_SL_DEBUG
+#endif
+
+#ifdef MULTI_SL_DEBUG
+#include <string>
+FILE * mslLogFile = NULL;
+FILE * mslLog()
+{
+#ifdef MSC
+ std::string logFileName(getenv("TEMP"));
+ logFileName.append("\\msllayout.log");
+ if (mslLogFile == NULL) mslLogFile = fopen(logFileName.c_str(),"w");
+ else fflush(mslLogFile);
+ return mslLogFile;
+#else
+ return stdout;
+#endif
+}
+#endif
+// =======================================================================
+
+// TODO: ask the glyph directly, for now we need this method because of #i99367#
+// true if a codepoint doesn't influence the logical text width
+bool IsDiacritic( sal_UCS4 nChar )
+{
+ // shortcut abvious non-diacritics
+ if( nChar < 0x0300 )
+ return false;
+ if( nChar >= 0x2100 )
+ return false;
+
+ // TODO: #i105058# use icu uchar.h's character classification instead of the handcrafted table
+ struct DiaRange { sal_UCS4 mnMin, mnEnd;};
+ static const DiaRange aRanges[] = {
+ {0x0300, 0x0370},
+ {0x0590, 0x05BE}, {0x05BF, 0x05C0}, {0x05C1, 0x05C3}, {0x05C4, 0x05C6}, {0x05C7, 0x05C8},
+ {0x0610, 0x061B}, {0x064B, 0x0660}, {0x0670, 0x0671}, {0x06D6, 0x06DD}, {0x06DF, 0x06E5}, {0x06E7, 0x06E9}, {0x06EA,0x06EF},
+ {0x0730, 0x074D}, {0x07A6, 0x07B1}, {0x07EB, 0x07F4},
+#if 0 // all known fonts have zero-width diacritics already, so no need to query it
+ {0x0900, 0x0904}, {0x093C, 0x093D}, {0x0941, 0x0948}, {0x094D, 0x0950}, {0x0951, 0x0958},
+ {0x0980, 0x0985}, {0x09BC, 0x09BD}, {0x09C1, 0x09C7}, {0x09CD, 0x09CE}, {0x09E2, 0x09E6},
+ {0x0A00, 0x0A05}, {0x0A3C, 0x0A59}, //...
+#endif
+ {0x1DC0, 0x1E00},
+ {0x205F, 0x2070}, {0x20D0, 0x2100},
+ {0xFB1E, 0xFB1F}
+ };
+
+ // TODO: almost anything is faster than an O(n) search
+ static const int nCount = sizeof(aRanges) / sizeof(*aRanges);
+ const DiaRange* pRange = &aRanges[0];
+ for( int i = nCount; --i >= 0; ++pRange )
+ if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
+ return true;
+
+ return false;
+}
+
+// =======================================================================
+
+int GetVerticalFlags( sal_UCS4 nChar )
+{
+ if( (nChar >= 0x1100 && nChar <= 0x11f9) // Hangul Jamo
+ || (nChar == 0x2030 || nChar == 0x2031) // per mille sign
+ || (nChar >= 0x3000 && nChar <= 0xfaff) // unified CJK
+ || (nChar >= 0xfe20 && nChar <= 0xfe6f) // CJK compatibility
+ || (nChar >= 0xff00 && nChar <= 0xfffd) ) // other CJK
+ {
+ /* #i52932# remember:
+ nChar == 0x2010 || nChar == 0x2015
+ nChar == 0x2016 || nChar == 0x2026
+ are GF_NONE also, but already handled in the outer if condition
+ */
+ if((nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012)
+ || (nChar == 0xFF3B || nChar == 0xFF3D)
+ || (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms
+ || (nChar == 0xFFE3) )
+ return GF_NONE; // not rotated
+ else if( nChar == 0x30fc )
+ return GF_ROTR; // right
+ return GF_ROTL; // left
+ }
+ else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs
+ return GF_ROTL; // left
+
+ return GF_NONE; // not rotated as default
+}
+
+// -----------------------------------------------------------------------
+
+sal_UCS4 GetVerticalChar( sal_UCS4 )
+{
+ return 0; // #i14788# input method is responsible vertical char changes
+
+#if 0
+ int nVert = 0;
+ switch( nChar )
+ {
+ // #104627# special treatment for some unicodes
+ case 0x002C: nVert = 0x3001; break;
+ case 0x002E: nVert = 0x3002; break;
+ /*
+ // to few fonts have the compatibility forms, using
+ // them will then cause more trouble than good
+ // TODO: decide on a font specific basis
+ case 0x2018: nVert = 0xFE41; break;
+ case 0x2019: nVert = 0xFE42; break;
+ case 0x201C: nVert = 0xFE43; break;
+ case 0x201D: nVert = 0xFE44; break;
+ // CJK compatibility forms
+ case 0x2025: nVert = 0xFE30; break;
+ case 0x2014: nVert = 0xFE31; break;
+ case 0x2013: nVert = 0xFE32; break;
+ case 0x005F: nVert = 0xFE33; break;
+ case 0x0028: nVert = 0xFE35; break;
+ case 0x0029: nVert = 0xFE36; break;
+ case 0x007B: nVert = 0xFE37; break;
+ case 0x007D: nVert = 0xFE38; break;
+ case 0x3014: nVert = 0xFE39; break;
+ case 0x3015: nVert = 0xFE3A; break;
+ case 0x3010: nVert = 0xFE3B; break;
+ case 0x3011: nVert = 0xFE3C; break;
+ case 0x300A: nVert = 0xFE3D; break;
+ case 0x300B: nVert = 0xFE3E; break;
+ case 0x3008: nVert = 0xFE3F; break;
+ case 0x3009: nVert = 0xFE40; break;
+ case 0x300C: nVert = 0xFE41; break;
+ case 0x300D: nVert = 0xFE42; break;
+ case 0x300E: nVert = 0xFE43; break;
+ case 0x300F: nVert = 0xFE44; break;
+ */
+ }
+
+ return nVert;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+VCL_DLLPUBLIC sal_UCS4 GetMirroredChar( sal_UCS4 nChar )
+{
+ nChar = u_charMirror( nChar );
+ return nChar;
+}
+
+// -----------------------------------------------------------------------
+
+// Get simple approximations for unicodes
+const char* GetAutofallback( sal_UCS4 nChar )
+{
+ const char* pStr = NULL;
+ switch( nChar )
+ {
+ case 0x01C0:
+ case 0x2223:
+ case 0x2758:
+ pStr = "|"; break;
+ case 0x02DC:
+ pStr = "~"; break;
+ case 0x037E:
+ pStr = ";"; break;
+ case 0x2000:
+ case 0x2001:
+ case 0x2002:
+ case 0x2003:
+ case 0x2004:
+ case 0x2005:
+ case 0x2006:
+ case 0x2007:
+ case 0x2008:
+ case 0x2009:
+ case 0x200A:
+ case 0x202F:
+ pStr = " "; break;
+ case 0x2010:
+ case 0x2011:
+ case 0x2012:
+ case 0x2013:
+ case 0x2014:
+ pStr = "-"; break;
+ case 0x2015:
+ pStr = "--"; break;
+ case 0x2016:
+ pStr = "||"; break;
+ case 0x2017:
+ pStr = "_"; break;
+ case 0x2018:
+ case 0x2019:
+ case 0x201B:
+ pStr = "\'"; break;
+ case 0x201A:
+ pStr = ","; break;
+ case 0x201C:
+ case 0x201D:
+ case 0x201E:
+ case 0x201F:
+ case 0x2033:
+ pStr = "\""; break;
+ case 0x2039:
+ pStr = "<"; break;
+ case 0x203A:
+ pStr = ">"; break;
+ case 0x203C:
+ pStr = "!!"; break;
+ case 0x203D:
+ pStr = "?"; break;
+ case 0x2044:
+ case 0x2215:
+ pStr = "/"; break;
+ case 0x2048:
+ pStr = "?!"; break;
+ case 0x2049:
+ pStr = "!?"; break;
+ case 0x2216:
+ pStr = "\\"; break;
+ case 0x2217:
+ pStr = "*"; break;
+ case 0x2236:
+ pStr = ":"; break;
+ case 0x2264:
+ pStr = "<="; break;
+ case 0x2265:
+ pStr = "<="; break;
+ case 0x2303:
+ pStr = "^"; break;
+ }
+
+ return pStr;
+}
+
+// -----------------------------------------------------------------------
+
+sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
+{
+ // currently only conversion from ASCII digits is interesting
+ if( (nChar < '0') || ('9' < nChar) )
+ return nChar;
+
+ int nOffset;
+ // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
+ // CAVEAT! To some like Mongolian MS assigned the same primary language
+ // although the script type is different!
+ switch( eLang & LANGUAGE_MASK_PRIMARY )
+ {
+ default:
+ nOffset = 0;
+ break;
+ case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0660 - '0'; // arabic-indic digits
+ break;
+ case LANGUAGE_FARSI & LANGUAGE_MASK_PRIMARY:
+ case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
+ case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
+ case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
+ break;
+ case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x09E6 - '0'; // bengali
+ break;
+ case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0966 - '0'; // devanagari
+ break;
+ case LANGUAGE_AMHARIC_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
+ case LANGUAGE_TIGRIGNA_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
+ // TODO case:
+ nOffset = 0x1369 - '0'; // ethiopic
+ break;
+ case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0AE6 - '0'; // gujarati
+ break;
+#ifdef LANGUAGE_GURMUKHI // TODO case:
+ case LANGUAGE_GURMUKHI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0A66 - '0'; // gurmukhi
+ break;
+#endif
+ case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0CE6 - '0'; // kannada
+ break;
+ case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x17E0 - '0'; // khmer
+ break;
+ case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0ED0 - '0'; // lao
+ break;
+ case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0D66 - '0'; // malayalam
+ break;
+ case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY:
+ if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
+ nOffset = 0x1810 - '0'; // mongolian
+ else
+ nOffset = 0; // mongolian cyrillic
+ break;
+ case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x1040 - '0'; // myanmar
+ break;
+ case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0B66 - '0'; // oriya
+ break;
+ case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0BE7 - '0'; // tamil
+ break;
+ case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0C66 - '0'; // telugu
+ break;
+ case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0E50 - '0'; // thai
+ break;
+ case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0F20 - '0'; // tibetan
+ break;
+#if 0 // TODO: use language type for these digit substitutions?
+ // TODO case:
+ nOffset = 0x2776 - '0'; // dingbat circled
+ break;
+ // TODO case:
+ nOffset = 0x2070 - '0'; // superscript
+ break;
+ // TODO case:
+ nOffset = 0x2080 - '0'; // subscript
+ break;
+#endif
+ }
+
+ nChar += nOffset;
+ return nChar;
+}
+
+// -----------------------------------------------------------------------
+
+inline bool IsControlChar( sal_UCS4 cChar )
+{
+ // C0 control characters
+ if( (0x0001 <= cChar) && (cChar <= 0x001F) )
+ return true;
+ // formatting characters
+ if( (0x200E <= cChar) && (cChar <= 0x200F) )
+ return true;
+ if( (0x2028 <= cChar) && (cChar <= 0x202E) )
+ return true;
+ // deprecated formatting characters
+ if( (0x206A <= cChar) && (cChar <= 0x206F) )
+ return true;
+ if( (0x2060 == cChar) )
+ return true;
+ // byte order markers and invalid unicode
+ if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
+ return true;
+ return false;
+}
+
+// =======================================================================
+
+bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
+{
+ // check if charpos could extend current run
+ int nIndex = maRuns.size();
+ if( nIndex >= 2 )
+ {
+ int nRunPos0 = maRuns[ nIndex-2 ];
+ int nRunPos1 = maRuns[ nIndex-1 ];
+ if( ((nCharPos + bRTL) == nRunPos1)
+ && ((nRunPos0 > nRunPos1) == bRTL) )
+ {
+ // extend current run by new charpos
+ maRuns[ nIndex-1 ] = nCharPos + !bRTL;
+ return false;
+ }
+ // ignore new charpos when it is in current run
+ if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
+ return false;
+ if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
+ return false;
+ }
+
+ // else append a new run consisting of the new charpos
+ maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
+ maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
+{
+ if( nCharPos0 == nCharPos1 )
+ return false;
+
+ // swap if needed
+ if( bRTL == (nCharPos0 < nCharPos1) )
+ {
+ int nTemp = nCharPos0;
+ nCharPos0 = nCharPos1;
+ nCharPos1 = nTemp;
+ }
+
+ // append new run
+ maRuns.push_back( nCharPos0 );
+ maRuns.push_back( nCharPos1 );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
+{
+ if( mnRunIndex >= (int)maRuns.size() )
+ return false;
+
+ int nMinCharPos = maRuns[ mnRunIndex+0 ];
+ int nEndCharPos = maRuns[ mnRunIndex+1 ];
+ if( nMinCharPos > nEndCharPos ) // reversed in RTL case
+ {
+ int nTemp = nMinCharPos;
+ nMinCharPos = nEndCharPos;
+ nEndCharPos = nTemp;
+ }
+
+ if( nCharPos < nMinCharPos )
+ return false;
+ if( nCharPos >= nEndCharPos )
+ return false;
+ return true;
+}
+
+bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
+{
+ bool bRet = false;
+ int nRunIndex = mnRunIndex;
+
+ ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
+
+ pThis->ResetPos();
+
+ for (size_t i = 0; i < maRuns.size(); i+=2)
+ {
+ if( (bRet = PosIsInRun( nCharPos )) == true )
+ break;
+ pThis->NextRun();
+ }
+
+ pThis->mnRunIndex = nRunIndex;
+ return bRet;
+}
+
+
+// -----------------------------------------------------------------------
+
+bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
+{
+ // negative nCharPos => reset to first run
+ if( *nCharPos < 0 )
+ mnRunIndex = 0;
+
+ // return false when all runs completed
+ if( mnRunIndex >= (int)maRuns.size() )
+ return false;
+
+ int nRunPos0 = maRuns[ mnRunIndex+0 ];
+ int nRunPos1 = maRuns[ mnRunIndex+1 ];
+ *bRightToLeft = (nRunPos0 > nRunPos1);
+
+ if( *nCharPos < 0 )
+ {
+ // get first valid nCharPos in run
+ *nCharPos = nRunPos0;
+ }
+ else
+ {
+ // advance to next nCharPos for LTR case
+ if( !*bRightToLeft )
+ ++(*nCharPos);
+
+ // advance to next run if current run is completed
+ if( *nCharPos == nRunPos1 )
+ {
+ if( (mnRunIndex += 2) >= (int)maRuns.size() )
+ return false;
+ nRunPos0 = maRuns[ mnRunIndex+0 ];
+ nRunPos1 = maRuns[ mnRunIndex+1 ];
+ *bRightToLeft = (nRunPos0 > nRunPos1);
+ *nCharPos = nRunPos0;
+ }
+ }
+
+ // advance to next nCharPos for RTL case
+ if( *bRightToLeft )
+ --(*nCharPos);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
+{
+ if( mnRunIndex >= (int)maRuns.size() )
+ return false;
+
+ int nRunPos0 = maRuns[ mnRunIndex+0 ];
+ int nRunPos1 = maRuns[ mnRunIndex+1 ];
+ *bRightToLeft = (nRunPos1 < nRunPos0) ;
+ if( !*bRightToLeft )
+ {
+ *nMinRunPos = nRunPos0;
+ *nEndRunPos = nRunPos1;
+ }
+ else
+ {
+ *nMinRunPos = nRunPos1;
+ *nEndRunPos = nRunPos0;
+ }
+ return true;
+}
+
+// =======================================================================
+
+ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen,
+ int nMinCharPos, int nEndCharPos, int nFlags )
+:
+ mnFlags( nFlags ),
+ mnLength( nLen ),
+ mnMinCharPos( nMinCharPos ),
+ mnEndCharPos( nEndCharPos ),
+ mpStr( pStr ),
+ mpDXArray( NULL ),
+ mnLayoutWidth( 0 ),
+ mnOrientation( 0 )
+{
+ if( mnFlags & SAL_LAYOUT_BIDI_STRONG )
+ {
+ // handle strong BiDi mode
+
+ // do not bother to BiDi analyze strong LTR/RTL
+ // TODO: can we assume these strings do not have unicode control chars?
+ // if not remove the control characters from the runs
+ bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
+ AddRun( mnMinCharPos, mnEndCharPos, bRTL );
+ }
+ else
+ {
+ // handle weak BiDi mode
+
+ UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
+ if( mnFlags & SAL_LAYOUT_BIDI_RTL )
+ nLevel = UBIDI_DEFAULT_RTL;
+
+ // prepare substring for BiDi analysis
+ // TODO: reuse allocated pParaBidi
+ UErrorCode rcI18n = U_ZERO_ERROR;
+ UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
+ if( !pParaBidi )
+ return;
+ ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n ); // UChar != sal_Unicode in MinGW
+
+ UBiDi* pLineBidi = pParaBidi;
+ int nSubLength = mnEndCharPos - mnMinCharPos;
+ if( nSubLength != mnLength )
+ {
+ pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
+ ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
+ }
+
+ // run BiDi algorithm
+ const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
+ //maRuns.resize( 2 * nRunCount );
+ for( int i = 0; i < nRunCount; ++i )
+ {
+ int32_t nMinPos, nLength;
+ const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
+ const int nPos0 = nMinPos + mnMinCharPos;
+ const int nPos1 = nPos0 + nLength;
+
+ const bool bRTL = (nDir == UBIDI_RTL);
+ AddRun( nPos0, nPos1, bRTL );
+ }
+
+ // cleanup BiDi engine
+ if( pLineBidi != pParaBidi )
+ ubidi_close( pLineBidi );
+ ubidi_close( pParaBidi );
+ }
+
+ // prepare calls to GetNextPos/GetNextRun
+ maRuns.ResetPos();
+}
+
+// -----------------------------------------------------------------------
+
+// add a run after splitting it up to get rid of control chars
+void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
+{
+ DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
+
+ // remove control characters from runs by splitting them up
+ if( !bRTL )
+ {
+ for( int i = nCharPos0; i < nCharPos1; ++i )
+ if( IsControlChar( mpStr[i] ) )
+ {
+ // add run until control char
+ maRuns.AddRun( nCharPos0, i, bRTL );
+ nCharPos0 = i + 1;
+ }
+ }
+ else
+ {
+ for( int i = nCharPos1; --i >= nCharPos0; )
+ if( IsControlChar( mpStr[i] ) )
+ {
+ // add run until control char
+ maRuns.AddRun( i+1, nCharPos1, bRTL );
+ nCharPos1 = i;
+ }
+ }
+
+ // add remainder of run
+ maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplLayoutArgs::PrepareFallback()
+{
+ // short circuit if no fallback is needed
+ if( maReruns.IsEmpty() )
+ {
+ maRuns.Clear();
+ return false;
+ }
+
+ // convert the fallback requests to layout requests
+ bool bRTL;
+ int nMin, nEnd;
+
+ // get the individual fallback requests
+ typedef std::vector<int> IntVector;
+ IntVector aPosVector;
+ aPosVector.reserve( mnLength );
+ maReruns.ResetPos();
+ for(; maReruns.GetRun( &nMin, &nEnd, &bRTL ); maReruns.NextRun() )
+ for( int i = nMin; i < nEnd; ++i )
+ aPosVector.push_back( i );
+ maReruns.Clear();
+
+ // sort the individual fallback requests
+ std::sort( aPosVector.begin(), aPosVector.end() );
+
+ // adjust fallback runs to have the same order and limits of the original runs
+ ImplLayoutRuns aNewRuns;
+ maRuns.ResetPos();
+ for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
+ {
+ if( !bRTL) {
+ IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
+ for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
+ aNewRuns.AddPos( *it, bRTL );
+ } else {
+ IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
+ while( (it != aPosVector.begin()) && (*--it >= nMin) )
+ aNewRuns.AddPos( *it, bRTL );
+ }
+ }
+
+ maRuns = aNewRuns; // TODO: use vector<>::swap()
+ maRuns.ResetPos();
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
+{
+ bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
+ maRuns.NextRun();
+ return bValid;
+}
+
+// =======================================================================
+
+SalLayout::SalLayout()
+: mnMinCharPos( -1 ),
+ mnEndCharPos( -1 ),
+ mnLayoutFlags( 0 ),
+ mnUnitsPerPixel( 1 ),
+ mnOrientation( 0 ),
+ mnRefCount( 1 ),
+ maDrawOffset( 0, 0 )
+{}
+
+// -----------------------------------------------------------------------
+
+SalLayout::~SalLayout()
+{}
+
+// -----------------------------------------------------------------------
+
+void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ mnMinCharPos = rArgs.mnMinCharPos;
+ mnEndCharPos = rArgs.mnEndCharPos;
+ mnLayoutFlags = rArgs.mnFlags;
+ mnOrientation = rArgs.mnOrientation;
+}
+
+// -----------------------------------------------------------------------
+
+void SalLayout::Reference() const
+{
+ // TODO: protect when multiple threads can access this
+ ++mnRefCount;
+}
+
+// -----------------------------------------------------------------------
+
+void SalLayout::Release() const
+{
+ // TODO: protect when multiple threads can access this
+ if( --mnRefCount > 0 )
+ return;
+ // const_cast because some compilers violate ANSI C++ spec
+ delete const_cast<SalLayout*>(this);
+}
+
+// -----------------------------------------------------------------------
+
+Point SalLayout::GetDrawPosition( const Point& rRelative ) const
+{
+ Point aPos = maDrawBase;
+ Point aOfs = rRelative + maDrawOffset;
+
+ if( mnOrientation == 0 )
+ aPos += aOfs;
+ else
+ {
+ // cache trigonometric results
+ static int nOldOrientation = 0;
+ static double fCos = 1.0, fSin = 0.0;
+ if( nOldOrientation != mnOrientation )
+ {
+ nOldOrientation = mnOrientation;
+ double fRad = mnOrientation * (M_PI / 1800.0);
+ fCos = cos( fRad );
+ fSin = sin( fRad );
+ }
+
+ double fX = aOfs.X();
+ double fY = aOfs.Y();
+ long nX = static_cast<long>( +fCos * fX + fSin * fY );
+ long nY = static_cast<long>( +fCos * fY - fSin * fX );
+ aPos += Point( nX, nY );
+ }
+
+ return aPos;
+}
+
+// -----------------------------------------------------------------------
+
+// returns asian kerning values in quarter of character width units
+// to enable automatic halfwidth substitution for fullwidth punctuation
+// return value is negative for l, positive for r, zero for neutral
+
+// If the range doesn't match in 0x3000 and 0x30FB, please change
+// also ImplCalcKerning.
+
+int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
+{
+ // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
+ static signed char nTable[0x30] =
+ {
+ 0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
+ +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
+ };
+
+ int nResult = 0;
+ if( (c >= 0x3000) && (c < 0x3030) )
+ nResult = nTable[ c - 0x3000 ];
+ else switch( c )
+ {
+#if 0 // TODO: enable it for real-fixed-width fonts?
+ case ':': case ';': case '!':
+ if( !bVertical )
+ nResult = bLeft ? -1 : +1; // 25% left and right
+ break;
+#endif
+ case 0x30FB:
+ nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
+ break;
+ case 0x2019: case 0x201D:
+ case 0xFF01: case 0xFF09: case 0xFF0C:
+ case 0xFF1A: case 0xFF1B:
+ nResult = -2;
+ break;
+ case 0x2018: case 0x201C:
+ case 0xFF08:
+ nResult = +2;
+ break;
+ default:
+ break;
+ }
+
+ return nResult;
+}
+
+// -----------------------------------------------------------------------
+
+bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
+ ::basegfx::B2DPolyPolygonVector& rVector ) const
+{
+ bool bAllOk = true;
+ bool bOneOk = false;
+
+ Point aPos;
+ ::basegfx::B2DPolyPolygon aGlyphOutline;
+ for( int nStart = 0;;)
+ {
+ sal_GlyphId nLGlyph;
+ if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
+ break;
+
+ // get outline of individual glyph, ignoring "empty" glyphs
+ bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
+ bAllOk &= bSuccess;
+ bOneOk |= bSuccess;
+ // only add non-empty outlines
+ if( bSuccess && (aGlyphOutline.count() > 0) )
+ {
+ if( aPos.X() || aPos.Y() )
+ {
+ aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
+ }
+
+ // insert outline at correct position
+ rVector.push_back( aGlyphOutline );
+ }
+ }
+
+ return (bAllOk & bOneOk);
+}
+
+// -----------------------------------------------------------------------
+
+bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
+{
+ bool bRet = false;
+ rRect.SetEmpty();
+
+ Point aPos;
+ Rectangle aRectangle;
+ for( int nStart = 0;;)
+ {
+ sal_GlyphId nLGlyph;
+ if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
+ break;
+
+ // get bounding rectangle of individual glyph
+ if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
+ {
+ // merge rectangle
+ aRectangle += aPos;
+ rRect.Union( aRectangle );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const
+{
+ bool bRet = false;
+ if( nGlyph & GF_ISCHAR )
+ {
+ long nChar = nGlyph & GF_IDXMASK;
+ bRet = (nChar <= 0x0020) // blank
+ //|| (nChar == 0x00A0) // non breaking space
+ || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
+ || (nChar == 0x3000); // ideographic space
+ }
+ else
+ bRet = ((nGlyph & GF_IDXMASK) == 3);
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+const ImplFontData* SalLayout::GetFallbackFontData( sal_GlyphId /*nGlyphId*/ ) const
+{
+#if 0
+ int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT
+ assert( nFallbackLevel == 0 );
+#endif
+ return NULL;
+}
+
+// =======================================================================
+
+GenericSalLayout::GenericSalLayout()
+: mpGlyphItems(0),
+ mnGlyphCount(0),
+ mnGlyphCapacity(0)
+{}
+
+// -----------------------------------------------------------------------
+
+GenericSalLayout::~GenericSalLayout()
+{
+ delete[] mpGlyphItems;
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
+{
+ // TODO: use std::list<GlyphItem>
+ if( mnGlyphCount >= mnGlyphCapacity )
+ {
+ mnGlyphCapacity += 16 + 3 * mnGlyphCount;
+ GlyphItem* pNewGI = new GlyphItem[ mnGlyphCapacity ];
+ if( mpGlyphItems )
+ {
+ for( int i = 0; i < mnGlyphCount; ++i )
+ pNewGI[ i ] = mpGlyphItems[ i ];
+ delete[] mpGlyphItems;
+ }
+ mpGlyphItems = pNewGI;
+ }
+
+ mpGlyphItems[ mnGlyphCount++ ] = rGlyphItem;
+}
+
+// -----------------------------------------------------------------------
+
+bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
+{
+ // initialize character extents buffer
+ int nCharCount = mnEndCharPos - mnMinCharPos;
+ for( int n = 0; n < nCharCount; ++n )
+ pCharWidths[n] = 0;
+
+ // determine cluster extents
+ const GlyphItem* const pEnd = mpGlyphItems + mnGlyphCount;
+ for( const GlyphItem* pG = mpGlyphItems; pG < pEnd; ++pG )
+ {
+ // use cluster start to get char index
+ if( !pG->IsClusterStart() )
+ continue;
+
+ int n = pG->mnCharPos;
+ if( n >= mnEndCharPos )
+ continue;
+ n -= mnMinCharPos;
+ if( n < 0 )
+ continue;
+
+ // left glyph in cluster defines default extent
+ long nXPosMin = pG->maLinearPos.X();
+ long nXPosMax = nXPosMin + pG->mnNewWidth;
+
+ // calculate right x-position for this glyph cluster
+ // break if no more glyphs in layout
+ // break at next glyph cluster start
+ while( (pG+1 < pEnd) && !pG[1].IsClusterStart() )
+ {
+ // advance to next glyph in cluster
+ ++pG;
+
+ if( pG->IsDiacritic() )
+ continue; // ignore diacritics
+ // get leftmost x-extent of this glyph
+ long nXPos = pG->maLinearPos.X();
+ if( nXPosMin > nXPos )
+ nXPosMin = nXPos;
+
+ // get rightmost x-extent of this glyph
+ nXPos += pG->mnNewWidth;
+ if( nXPosMax < nXPos )
+ nXPosMax = nXPos;
+ }
+
+ // when the current cluster overlaps with the next one assume
+ // rightmost cluster edge is the leftmost edge of next cluster
+ // for clusters that do not have x-sorted glyphs
+ // TODO: avoid recalculation of left bound in next cluster iteration
+ for( const GlyphItem* pN = pG; ++pN < pEnd; )
+ {
+ if( pN->IsClusterStart() )
+ break;
+ if( pN->IsDiacritic() )
+ continue; // ignore diacritics
+ if( nXPosMax > pN->maLinearPos.X() )
+ nXPosMax = pN->maLinearPos.X();
+ }
+ if( nXPosMax < nXPosMin )
+ nXPosMin = nXPosMax = 0;
+
+ // character width is sum of glyph cluster widths
+ pCharWidths[n] += nXPosMax - nXPosMin;
+ }
+
+ // TODO: distribute the cluster width proportionally to the characters
+ // clusters (e.g. ligatures) correspond to more than one char index,
+ // so some character widths are still uninitialized. This is solved
+ // by setting the first charwidth of the cluster to the cluster width
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
+{
+ if( pCharWidths )
+ if( !GetCharWidths( pCharWidths ) )
+ return 0;
+
+ long nWidth = GetTextWidth();
+ return nWidth;
+}
+
+// -----------------------------------------------------------------------
+
+// the text width is the maximum logical extent of all glyphs
+long GenericSalLayout::GetTextWidth() const
+{
+ if( mnGlyphCount <= 0 )
+ return 0;
+
+ // initialize the extent
+ long nMinPos = 0;
+ long nMaxPos = 0;
+
+ const GlyphItem* pG = mpGlyphItems;
+ for( int i = mnGlyphCount; --i >= 0; ++pG )
+ {
+ // update the text extent with the glyph extent
+ long nXPos = pG->maLinearPos.X();
+ if( nMinPos > nXPos )
+ nMinPos = nXPos;
+ nXPos += pG->mnNewWidth;
+ if( nMaxPos < nXPos )
+ nMaxPos = nXPos;
+ }
+
+ long nWidth = nMaxPos - nMinPos;
+ return nWidth;
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+
+ if( rArgs.mpDXArray )
+ ApplyDXArray( rArgs );
+ else if( rArgs.mnLayoutWidth )
+ Justify( rArgs.mnLayoutWidth );
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
+{
+ if( mnGlyphCount <= 0 )
+ return;
+
+ // determine cluster boundaries and x base offset
+ const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+ int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
+ int i, n;
+ long nBasePointX = -1;
+ if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
+ nBasePointX = 0;
+ for( i = 0; i < nCharCount; ++i )
+ pLogCluster[ i ] = -1;
+ GlyphItem* pG = mpGlyphItems;
+ for( i = 0; i < mnGlyphCount; ++i, ++pG )
+ {
+ n = pG->mnCharPos - rArgs.mnMinCharPos;
+ if( (n < 0) || (nCharCount <= n) )
+ continue;
+ if( pLogCluster[ n ] < 0 )
+ pLogCluster[ n ] = i;
+ if( nBasePointX < 0 )
+ nBasePointX = pG->maLinearPos.X();
+ }
+ // retarget unresolved pLogCluster[n] to a glyph inside the cluster
+ // TODO: better do it while the deleted-glyph markers are still there
+ for( n = 0; n < nCharCount; ++n )
+ if( (i = pLogCluster[0]) >= 0 )
+ break;
+ if( n >= nCharCount )
+ return;
+ for( n = 0; n < nCharCount; ++n )
+ {
+ if( pLogCluster[ n ] < 0 )
+ pLogCluster[ n ] = i;
+ else
+ i = pLogCluster[ n ];
+ }
+
+ // calculate adjusted cluster widths
+ sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( mnGlyphCount * sizeof(long) );
+ for( i = 0; i < mnGlyphCount; ++i )
+ pNewGlyphWidths[ i ] = 0;
+
+ bool bRTL;
+ for( int nCharPos = i = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
+ {
+ n = nCharPos - rArgs.mnMinCharPos;
+ if( (n < 0) || (nCharCount <= n) ) continue;
+
+ if( pLogCluster[ n ] >= 0 )
+ i = pLogCluster[ n ];
+ if( i >= 0 )
+ {
+ long nDelta = rArgs.mpDXArray[ n ] ;
+ if( n > 0 )
+ nDelta -= rArgs.mpDXArray[ n-1 ];
+ pNewGlyphWidths[ i ] += nDelta * mnUnitsPerPixel;
+ }
+ }
+
+ // move cluster positions using the adjusted widths
+ long nDelta = 0;
+ long nNewPos = 0;
+ pG = mpGlyphItems;
+ for( i = 0; i < mnGlyphCount; ++i, ++pG )
+ {
+ if( pG->IsClusterStart() )
+ {
+ // calculate original and adjusted cluster width
+ int nOldClusterWidth = pG->mnNewWidth;
+ int nNewClusterWidth = pNewGlyphWidths[i];
+ GlyphItem* pClusterG = pG + 1;
+ for( int j = i; ++j < mnGlyphCount; ++pClusterG )
+ {
+ if( pClusterG->IsClusterStart() )
+ break;
+ if( !pClusterG->IsDiacritic() ) // #i99367# ignore diacritics
+ nOldClusterWidth += pClusterG->mnNewWidth;
+ nNewClusterWidth += pNewGlyphWidths[j];
+ }
+ const int nDiff = nNewClusterWidth - nOldClusterWidth;
+
+ // adjust cluster glyph widths and positions
+ nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X());
+ if( !pG->IsRTLGlyph() )
+ {
+ // for LTR case extend rightmost glyph in cluster
+ pClusterG[-1].mnNewWidth += nDiff;
+ }
+ else
+ {
+ // right align cluster in new space for RTL case
+ pG->mnNewWidth += nDiff;
+ nDelta += nDiff;
+ }
+
+ nNewPos += nNewClusterWidth;
+ }
+
+ pG->maLinearPos.X() += nDelta;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::Justify( long nNewWidth )
+{
+ nNewWidth *= mnUnitsPerPixel;
+ int nOldWidth = GetTextWidth();
+ if( !nOldWidth || nNewWidth==nOldWidth )
+ return;
+
+ // find rightmost glyph, it won't get stretched
+ GlyphItem* pGRight = mpGlyphItems + mnGlyphCount - 1;
+
+ // count stretchable glyphs
+ GlyphItem* pG;
+ int nStretchable = 0;
+ int nMaxGlyphWidth = 0;
+ for( pG = mpGlyphItems; pG < pGRight; ++pG )
+ {
+ if( !pG->IsDiacritic() )
+ ++nStretchable;
+ if( nMaxGlyphWidth < pG->mnOrigWidth )
+ nMaxGlyphWidth = pG->mnOrigWidth;
+ }
+
+ // move rightmost glyph to requested position
+ nOldWidth -= pGRight->mnOrigWidth;
+ if( nOldWidth <= 0 )
+ return;
+ if( nNewWidth < nMaxGlyphWidth)
+ nNewWidth = nMaxGlyphWidth;
+ nNewWidth -= pGRight->mnOrigWidth;
+ pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
+
+ // justify glyph widths and positions
+ int nDiffWidth = nNewWidth - nOldWidth;
+ if( nDiffWidth >= 0) // expanded case
+ {
+ // expand width by distributing space between glyphs evenly
+ int nDeltaSum = 0;
+ for( pG = mpGlyphItems; pG < pGRight; ++pG )
+ {
+ // move glyph to justified position
+ pG->maLinearPos.X() += nDeltaSum;
+
+ // do not stretch non-stretchable glyphs
+ if( pG->IsDiacritic() || (nStretchable <= 0) )
+ continue;
+
+ // distribute extra space equally to stretchable glyphs
+ int nDeltaWidth = nDiffWidth / nStretchable--;
+ nDiffWidth -= nDeltaWidth;
+ pG->mnNewWidth += nDeltaWidth;
+ nDeltaSum += nDeltaWidth;
+ }
+ }
+ else // condensed case
+ {
+ // squeeze width by moving glyphs proportionally
+ double fSqueeze = (double)nNewWidth / nOldWidth;
+ for( pG = mpGlyphItems; ++pG < pGRight;)
+ {
+ int nX = pG->maLinearPos.X() - maBasePoint.X();
+ nX = (int)(nX * fSqueeze);
+ pG->maLinearPos.X() = nX + maBasePoint.X();
+ }
+ // adjust glyph widths to new positions
+ for( pG = mpGlyphItems; pG < pGRight; ++pG )
+ pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
+{
+ long nOffset = 0;
+
+ GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
+ for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
+ {
+ const int n = pG->mnCharPos;
+ if( n < nLength - 1)
+ {
+ // ignore code ranges that are not affected by asian punctuation compression
+ const sal_Unicode cHere = pStr[n];
+ if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
+ continue;
+ const sal_Unicode cNext = pStr[n+1];
+ if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
+ continue;
+
+ // calculate compression values
+ const bool bVertical = false;
+ long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
+ long nKernNext = -CalcAsianKerning( cNext, false, bVertical );
+
+ // apply punctuation compression to logical glyph widths
+ long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
+ if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
+ {
+ int nGlyphWidth = pG->mnOrigWidth;
+ nDelta = (nDelta * nGlyphWidth + 2) / 4;
+ if( pG+1 == pGEnd )
+ pG->mnNewWidth += nDelta;
+ nOffset += nDelta;
+ }
+ }
+
+ // adjust the glyph positions to the new glyph widths
+ if( pG+1 != pGEnd )
+ pG->maLinearPos.X() += nOffset;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
+{
+ // TODO: reimplement method when container type for GlyphItems changes
+
+ // skip if the kashida glyph in the font looks suspicious
+ if( nKashidaWidth <= 0 )
+ return;
+
+ // calculate max number of needed kashidas
+ const GlyphItem* pG1 = mpGlyphItems;
+ int nKashidaCount = 0, i;
+ for( i = 0; i < mnGlyphCount; ++i, ++pG1 )
+ {
+ // only inject kashidas in RTL contexts
+ if( !pG1->IsRTLGlyph() )
+ continue;
+ // no kashida-injection for blank justified expansion either
+ if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
+ continue;
+
+ // calculate gap, ignore if too small
+ const int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
+ // worst case is one kashida even for mini-gaps
+ if( 3 * nGapWidth >= nKashidaWidth )
+ nKashidaCount += 1 + (nGapWidth / nKashidaWidth);
+ }
+
+ if( !nKashidaCount )
+ return;
+
+ // reallocate glyph array for additional kashidas
+ // TODO: reuse array if additional glyphs would fit
+ mnGlyphCapacity = mnGlyphCount + nKashidaCount;
+ GlyphItem* pNewGlyphItems = new GlyphItem[ mnGlyphCapacity ];
+ GlyphItem* pG2 = pNewGlyphItems;
+ pG1 = mpGlyphItems;
+ for( i = mnGlyphCount; --i >= 0; ++pG1, ++pG2 )
+ {
+ // default action is to copy array element
+ *pG2 = *pG1;
+
+ // only inject kashida in RTL contexts
+ if( !pG1->IsRTLGlyph() )
+ continue;
+ // no kashida-injection for blank justified expansion either
+ if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
+ continue;
+
+ // calculate gap, skip if too small
+ int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
+ if( 3*nGapWidth < nKashidaWidth )
+ continue;
+
+ // fill gap with kashidas
+ nKashidaCount = 0;
+ Point aPos = pG1->maLinearPos;
+ aPos.X() -= nGapWidth; // cluster is already right aligned
+ for(; nGapWidth > 0; nGapWidth -= nKashidaWidth, ++nKashidaCount )
+ {
+ *(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos,
+ GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth );
+ aPos.X() += nKashidaWidth;
+ }
+
+ // fixup rightmost kashida for gap remainder
+ if( nGapWidth < 0 )
+ {
+ aPos.X() += nGapWidth;
+ if( nKashidaCount <= 1 )
+ nGapWidth /= 2; // for small gap move kashida to middle
+ pG2[-1].mnNewWidth += nGapWidth; // adjust kashida width to gap width
+ pG2[-1].maLinearPos.X() += nGapWidth;
+ }
+
+ // when kashidas were inserted move the original cluster
+ // to the right and shrink it to it's original width
+ *pG2 = *pG1;
+ pG2->maLinearPos.X() = aPos.X();
+ pG2->mnNewWidth = pG2->mnOrigWidth;
+ }
+
+ // use the new glyph array
+ DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" );
+ delete[] mpGlyphItems;
+ mpGlyphItems = pNewGlyphItems;
+ mnGlyphCount = pG2 - pNewGlyphItems;
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
+{
+ // initialize result array
+ long nXPos = -1;
+ int i;
+ for( i = 0; i < nMaxIndex; ++i )
+ pCaretXArray[ i ] = nXPos;
+
+ // calculate caret positions using glyph array
+ const GlyphItem* pG = mpGlyphItems;
+ for( i = mnGlyphCount; --i >= 0; ++pG )
+ {
+ nXPos = pG->maLinearPos.X();
+ long nXRight = nXPos + pG->mnOrigWidth;
+ int n = pG->mnCharPos;
+ int nCurrIdx = 2 * (n - mnMinCharPos);
+ if( !pG->IsRTLGlyph() )
+ {
+ // normal positions for LTR case
+ pCaretXArray[ nCurrIdx ] = nXPos;
+ pCaretXArray[ nCurrIdx+1 ] = nXRight;
+ }
+ else
+ {
+ // reverse positions for RTL case
+ pCaretXArray[ nCurrIdx ] = nXRight;
+ pCaretXArray[ nCurrIdx+1 ] = nXPos;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+{
+ int nCharCapacity = mnEndCharPos - mnMinCharPos;
+ sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
+ if( !GetCharWidths( pCharWidths ) )
+ return STRING_LEN;
+
+ long nWidth = 0;
+ for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
+ {
+ nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
+ if( nWidth >= nMaxWidth )
+ return i;
+ nWidth += nCharExtra;
+ }
+
+ return STRING_LEN;
+}
+
+// -----------------------------------------------------------------------
+
+int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
+ int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
+{
+ const GlyphItem* pG = mpGlyphItems + nStart;
+
+ // find next glyph in substring
+ for(; nStart < mnGlyphCount; ++nStart, ++pG )
+ {
+ int n = pG->mnCharPos;
+ if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
+ break;
+ }
+
+ // return zero if no more glyph found
+ if( nStart >= mnGlyphCount )
+ return 0;
+
+ // calculate absolute position in pixel units
+ Point aRelativePos = pG->maLinearPos - maBasePoint;
+
+ // find more glyphs which can be merged into one drawing instruction
+ int nCount = 0;
+ long nYPos = pG->maLinearPos.Y();
+ long nOldFlags = pG->mnGlyphIndex;
+ for(;;)
+ {
+ // update return data with glyph info
+ ++nCount;
+ *(pGlyphs++) = pG->mnGlyphIndex;
+ if( pCharPosAry )
+ *(pCharPosAry++) = pG->mnCharPos;
+ if( pGlyphAdvAry )
+ *pGlyphAdvAry = pG->mnNewWidth;
+
+ // break at end of glyph list
+ if( ++nStart >= mnGlyphCount )
+ break;
+ // break when enough glyphs
+ if( nCount >= nLen )
+ break;
+
+ long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X();
+ if( pGlyphAdvAry )
+ {
+ // override default advance width with correct value
+ *(pGlyphAdvAry++) = nGlyphAdvance;
+ }
+ else
+ {
+ // stop when next x-position is unexpected
+ if( pG->mnOrigWidth != nGlyphAdvance )
+ break;
+ }
+
+ // advance to next glyph
+ ++pG;
+
+ // stop when next y-position is unexpected
+ if( nYPos != pG->maLinearPos.Y() )
+ break;
+
+ // stop when no longer in string
+ int n = pG->mnCharPos;
+ if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
+ break;
+
+ // stop when glyph flags change
+ if( (nOldFlags ^ pG->mnGlyphIndex) & GF_FLAGMASK )
+ break;
+
+ nOldFlags = pG->mnGlyphIndex; // &GF_FLAGMASK not needed for test above
+ }
+
+ aRelativePos.X() /= mnUnitsPerPixel;
+ aRelativePos.Y() /= mnUnitsPerPixel;
+ rPos = GetDrawPosition( aRelativePos );
+
+ return nCount;
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
+{
+ if( nStart >= mnGlyphCount )
+ return;
+
+ GlyphItem* pG = mpGlyphItems + nStart;
+ // the nNewXPos argument determines the new cell position
+ // as RTL-glyphs are right justified in their cell
+ // the cell position needs to be adjusted to the glyph position
+ if( pG->IsRTLGlyph() )
+ nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
+ // calculate the x-offset to the old position
+ long nXDelta = nNewXPos - pG->maLinearPos.X();
+ // adjust all following glyph positions if needed
+ if( nXDelta != 0 )
+ {
+ GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
+ for(; pG < pGEnd; ++pG )
+ pG->maLinearPos.X() += nXDelta;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::DropGlyph( int nStart )
+{
+ if( nStart >= mnGlyphCount )
+ return;
+ GlyphItem* pG = mpGlyphItems + nStart;
+ pG->mnGlyphIndex = GF_DROPPED;
+ pG->mnCharPos = -1;
+}
+
+// -----------------------------------------------------------------------
+
+void GenericSalLayout::Simplify( bool bIsBase )
+{
+ const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
+
+ // remove dropped glyphs inplace
+ GlyphItem* pGDst = mpGlyphItems;
+ const GlyphItem* pGSrc = mpGlyphItems;
+ const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
+ for(; pGSrc < pGEnd; ++pGSrc )
+ {
+ if( pGSrc->mnGlyphIndex == nDropMarker )
+ continue;
+ if( pGDst != pGSrc )
+ *pGDst = *pGSrc;
+ ++pGDst;
+ }
+ mnGlyphCount = pGDst - mpGlyphItems;
+}
+
+// -----------------------------------------------------------------------
+
+// make sure GlyphItems are sorted left to right
+void GenericSalLayout::SortGlyphItems()
+{
+ // move cluster components behind their cluster start (especially for RTL)
+ // using insertion sort because the glyph items are "almost sorted"
+ const GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
+ for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
+ {
+ // find a cluster starting with a diacritic
+ if( !pG->IsDiacritic() )
+ continue;
+ if( !pG->IsClusterStart() )
+ continue;
+ for( GlyphItem* pBaseGlyph = pG; ++pBaseGlyph < pGEnd; )
+ {
+ // find the base glyph matching to the misplaced diacritic
+ if( pBaseGlyph->IsClusterStart() )
+ break;
+ if( pBaseGlyph->IsDiacritic() )
+ continue;
+
+ // found the matching base glyph
+ // => this base glyph becomes the new cluster start
+ const GlyphItem aDiacritic = *pG;
+ *pG = *pBaseGlyph;
+ *pBaseGlyph = aDiacritic;
+
+ // update glyph flags of swapped glyphitems
+ pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
+ pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
+ // prepare for checking next cluster
+ pG = pBaseGlyph;
+ break;
+ }
+ }
+}
+
+// =======================================================================
+
+MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const ImplFontData* pBaseFont )
+: SalLayout()
+, mnLevel( 1 )
+, mbInComplete( false )
+{
+ //maFallbackRuns[0].Clear();
+ mpFallbackFonts[ 0 ] = pBaseFont;
+ mpLayouts[ 0 ] = &rBaseLayout;
+ mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
+}
+
+void MultiSalLayout::SetInComplete(bool bInComplete)
+{
+ mbInComplete = bInComplete;
+ maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
+}
+
+// -----------------------------------------------------------------------
+
+MultiSalLayout::~MultiSalLayout()
+{
+ for( int i = 0; i < mnLevel; ++i )
+ mpLayouts[ i ]->Release();
+}
+
+// -----------------------------------------------------------------------
+
+bool MultiSalLayout::AddFallback( SalLayout& rFallback,
+ ImplLayoutRuns& rFallbackRuns, const ImplFontData* pFallbackFont )
+{
+ if( mnLevel >= MAX_FALLBACK )
+ return false;
+
+ mpFallbackFonts[ mnLevel ] = pFallbackFont;
+ mpLayouts[ mnLevel ] = &rFallback;
+ maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
+ ++mnLevel;
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ if( mnLevel <= 1 )
+ return false;
+ if (!mbInComplete)
+ maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+ ImplLayoutArgs aMultiArgs = rArgs;
+
+ if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
+ {
+ // for stretched text in a MultiSalLayout the target width needs to be
+ // distributed by individually adjusting its virtual character widths
+ long nTargetWidth = aMultiArgs.mnLayoutWidth;
+ nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
+ aMultiArgs.mnLayoutWidth = 0;
+
+ // we need to get the original unmodified layouts ready
+ for( int n = 0; n < mnLevel; ++n )
+ mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
+ // then we can measure the unmodified metrics
+ int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+ sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
+ FillDXArray( pJustificationArray );
+ // #i17359# multilayout is not simplified yet, so calculating the
+ // unjustified width needs handholding; also count the number of
+ // stretchable virtual char widths
+ long nOrigWidth = 0;
+ int nStretchable = 0;
+ for( int i = 0; i < nCharCount; ++i )
+ {
+ // convert array from widths to sum of widths
+ nOrigWidth += pJustificationArray[i];
+ if( pJustificationArray[i] > 0 )
+ ++nStretchable;
+ }
+
+ // now we are able to distribute the extra width over the virtual char widths
+ if( nOrigWidth && (nTargetWidth != nOrigWidth) )
+ {
+ int nDiffWidth = nTargetWidth - nOrigWidth;
+ int nWidthSum = 0;
+ for( int i = 0; i < nCharCount; ++i )
+ {
+ int nJustWidth = pJustificationArray[i];
+ if( (nJustWidth > 0) && (nStretchable > 0) )
+ {
+ int nDeltaWidth = nDiffWidth / nStretchable;
+ nJustWidth += nDeltaWidth;
+ nDiffWidth -= nDeltaWidth;
+ --nStretchable;
+ }
+ nWidthSum += nJustWidth;
+ pJustificationArray[i] = nWidthSum;
+ }
+ if( nWidthSum != nTargetWidth )
+ pJustificationArray[ nCharCount-1 ] = nTargetWidth;
+
+ // the justification array is still in base level units
+ // => convert it to pixel units
+ if( mnUnitsPerPixel > 1 )
+ {
+ for( int i = 0; i < nCharCount; ++i )
+ {
+ sal_Int32 nVal = pJustificationArray[ i ];
+ nVal += (mnUnitsPerPixel + 1) / 2;
+ pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
+ }
+ }
+
+ // change the mpDXArray temporarilly (just for the justification)
+ aMultiArgs.mpDXArray = pJustificationArray;
+ }
+ }
+
+ // Compute rtl flags, since in some scripts glyphs/char order can be
+ // reversed for a few character sequencies e.g. Myanmar
+ std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
+ rArgs.ResetPos();
+ bool bRtl;
+ int nRunStart, nRunEnd;
+ while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
+ {
+ if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
+ vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
+ }
+ rArgs.ResetPos();
+
+ // prepare "merge sort"
+ int nStartOld[ MAX_FALLBACK ];
+ int nStartNew[ MAX_FALLBACK ];
+ int nCharPos[ MAX_FALLBACK ];
+ sal_Int32 nGlyphAdv[ MAX_FALLBACK ];
+ int nValid[ MAX_FALLBACK ] = {0};
+
+ sal_GlyphId nDummy;
+ Point aPos;
+ int nLevel = 0, n;
+ for( n = 0; n < mnLevel; ++n )
+ {
+ // now adjust the individual components
+ if( n > 0 )
+ {
+ aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
+ aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
+ }
+ mpLayouts[n]->AdjustLayout( aMultiArgs );
+
+ // disable glyph-injection for glyph-fallback SalLayout iteration
+ mpLayouts[n]->DisableGlyphInjection( true );
+
+ // remove unused parts of component
+ if( n > 0 )
+ {
+ if (mbInComplete && (n == mnLevel-1))
+ mpLayouts[n]->Simplify( true );
+ else
+ mpLayouts[n]->Simplify( false );
+ }
+
+ // prepare merging components
+ nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
+ nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
+ nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] );
+#ifdef MULTI_SL_DEBUG
+ 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(), nGlyphAdv[nLevel], nCharPos[nLevel],
+ rArgs.mpStr[nCharPos[nLevel]]);
+#endif
+ if( (n > 0) && !nValid[ nLevel ] )
+ {
+ // an empty fallback layout can be released
+ mpLayouts[n]->Release();
+ }
+ else
+ {
+ // reshuffle used fallbacks if needed
+ if( nLevel != n )
+ {
+ mpLayouts[ nLevel ] = mpLayouts[ n ];
+ mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ];
+ maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
+ }
+ ++nLevel;
+ }
+ }
+ mnLevel = nLevel;
+
+ // merge the fallback levels
+ long nXPos = 0;
+ double fUnitMul = 1.0;
+ for( n = 0; n < nLevel; ++n )
+ maFallbackRuns[n].ResetPos();
+ int nActiveCharPos = nCharPos[0];
+ int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
+ rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
+ int nRunVisibleEndChar = nCharPos[0];
+ while( nValid[0] && (nLevel > 0))
+ {
+ // find best fallback level
+ for( n = 0; n < nLevel; ++n )
+ if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
+ // fallback level n wins when it requested no further fallback
+ break;
+ int nFBLevel = n;
+
+ if( n < nLevel )
+ {
+ // use base(n==0) or fallback(n>=1) level
+ fUnitMul = mnUnitsPerPixel;
+ fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
+ long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
+ mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
+ }
+ else
+ {
+ n = 0; // keep NotDef in base level
+ fUnitMul = 1.0;
+ }
+
+ if( n > 0 )
+ {
+ // drop the NotDef glyphs in the base layout run if a fallback run exists
+ while (
+ (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
+ (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
+ )
+ {
+ mpLayouts[0]->DropGlyph( nStartOld[0] );
+ nStartOld[0] = nStartNew[0];
+ nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
+ nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
+#ifdef MULTI_SL_DEBUG
+ if (nValid[0]) fprintf(mslLog(), "layout[0]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", nStartOld[0], nStartNew[0], aPos.X(), nGlyphAdv[0], nCharPos[0], rArgs.mpStr[nCharPos[0]]);
+#endif
+ if( !nValid[0] )
+ break;
+ }
+ }
+
+ // skip to end of layout run and calculate its advance width
+ int nRunAdvance = 0;
+ bool bKeepNotDef = (nFBLevel >= nLevel);
+ for(;;)
+ {
+ nRunAdvance += nGlyphAdv[n];
+
+ // proceed to next glyph
+ nStartOld[n] = nStartNew[n];
+ int nOrigCharPos = nCharPos[n];
+ nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
+ nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
+#ifdef MULTI_SL_DEBUG
+ if (nValid[n]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d a%d c%d %x\n", n, nStartOld[n], nStartNew[n], nGlyphAdv[n], nCharPos[n], rArgs.mpStr[nCharPos[n]]);
+#endif
+ // break after last glyph of active layout
+ if( !nValid[n] )
+ {
+ // performance optimization (when a fallback layout is no longer needed)
+ if( n >= nLevel-1 )
+ --nLevel;
+ break;
+ }
+
+ //If the next character is one which belongs to the next level, then we
+ //are finished here for now, and we'll pick up after the next level has
+ //been processed
+ if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
+ {
+ if (nOrigCharPos < nCharPos[n])
+ {
+ if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
+ break;
+ }
+ else if (nOrigCharPos > nCharPos[n])
+ {
+ if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
+ break;
+ }
+ }
+
+ // break at end of layout run
+ if( n > 0 )
+ {
+ // skip until end of fallback run
+ if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
+ break;
+ }
+ else
+ {
+ // break when a fallback is needed and available
+ bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
+ if( bNeedFallback )
+ if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
+ break;
+ // break when change from resolved to unresolved base layout run
+ if( bKeepNotDef && !bNeedFallback )
+ { maFallbackRuns[0].NextRun(); break; }
+ bKeepNotDef = bNeedFallback;
+ }
+ // check for reordered glyphs
+ if (aMultiArgs.mpDXArray &&
+ nRunVisibleEndChar < mnEndCharPos &&
+ nRunVisibleEndChar >= mnMinCharPos &&
+ nCharPos[n] < mnEndCharPos &&
+ nCharPos[n] >= mnMinCharPos)
+ {
+ if (vRtl[nActiveCharPos - mnMinCharPos])
+ {
+ if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
+ >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
+ {
+ nRunVisibleEndChar = nCharPos[n];
+ }
+ }
+ else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
+ <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
+ {
+ nRunVisibleEndChar = nCharPos[n];
+ }
+ }
+ }
+
+ // if a justification array is available
+ // => use it directly to calculate the corresponding run width
+ if( aMultiArgs.mpDXArray )
+ {
+ // the run advance is the width from the first char
+ // in the run to the first char in the next run
+ nRunAdvance = 0;
+#ifdef MULTI_SL_DEBUG
+ const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
+ int nOldRunAdv = 0;
+ int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
+ if( nDXIndex >= 0 )
+ nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
+ nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
+ if( nDXIndex >= 0 )
+ nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
+ if( !bLTR )
+ nOldRunAdv = -nOldRunAdv;
+#endif
+ if (vRtl[nActiveCharPos - mnMinCharPos])
+ {
+ if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
+ nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
+ if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
+ nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
+#ifdef MULTI_SL_DEBUG
+ fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
+#endif
+ }
+ else
+ {
+ if (nRunVisibleEndChar >= mnMinCharPos)
+ nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
+ if (nLastRunEndChar >= mnMinCharPos)
+ nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
+#ifdef MULTI_SL_DEBUG
+ fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
+#endif
+ }
+ nLastRunEndChar = nRunVisibleEndChar;
+ nRunVisibleEndChar = nCharPos[0];
+ // the requested width is still in pixel units
+ // => convert it to base level font units
+ nRunAdvance *= mnUnitsPerPixel;
+ }
+ else
+ {
+ // the measured width is still in fallback font units
+ // => convert it to base level font units
+ if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
+ nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
+ }
+
+ // calculate new x position (in base level units)
+ nXPos += nRunAdvance;
+
+ // prepare for next fallback run
+ nActiveCharPos = nCharPos[0];
+ // it essential that the runs don't get ahead of themselves and in the
+ // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
+ // have already been reached on the base level
+ for( int i = nFBLevel; --i >= 0;)
+ {
+ if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
+ {
+ if (bRtl)
+ {
+ if (nRunStart > nActiveCharPos)
+ maFallbackRuns[i].NextRun();
+ }
+ else
+ {
+ if (nRunEnd <= nActiveCharPos)
+ maFallbackRuns[i].NextRun();
+ }
+ }
+ }
+// if( !maFallbackRuns[i].PosIsInRun( nActiveCharPos ) )
+// maFallbackRuns[i].NextRun();
+ }
+
+ mpLayouts[0]->Simplify( true );
+
+ // reenable glyph-injection
+ for( n = 0; n < mnLevel; ++n )
+ mpLayouts[n]->DisableGlyphInjection( false );
+}
+
+// -----------------------------------------------------------------------
+
+void MultiSalLayout::InitFont() const
+{
+ if( mnLevel > 0 )
+ mpLayouts[0]->InitFont();
+}
+
+// -----------------------------------------------------------------------
+
+const ImplFontData* MultiSalLayout::GetFallbackFontData( sal_GlyphId nGlyphId ) const
+{
+ int nFallbackLevel = (nGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
+ return mpFallbackFonts[ nFallbackLevel ];
+}
+
+// -----------------------------------------------------------------------
+
+void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
+{
+ for( int i = mnLevel; --i >= 0; )
+ {
+ SalLayout& rLayout = *mpLayouts[ i ];
+ rLayout.DrawBase() += maDrawBase;
+ rLayout.DrawOffset() += maDrawOffset;
+ rLayout.InitFont();
+ rLayout.DrawText( rGraphics );
+ rLayout.DrawOffset() -= maDrawOffset;
+ rLayout.DrawBase() -= maDrawBase;
+ }
+ // NOTE: now the baselevel font is active again
+}
+
+ // -----------------------------------------------------------------------
+
+int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+{
+ if( mnLevel <= 0 )
+ return STRING_LEN;
+ if( mnLevel == 1 )
+ return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
+
+ int nCharCount = mnEndCharPos - mnMinCharPos;
+ sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
+ mpLayouts[0]->FillDXArray( pCharWidths );
+
+ for( int n = 1; n < mnLevel; ++n )
+ {
+ SalLayout& rLayout = *mpLayouts[ n ];
+ rLayout.FillDXArray( pCharWidths + nCharCount );
+ double fUnitMul = mnUnitsPerPixel;
+ fUnitMul /= rLayout.GetUnitsPerPixel();
+ for( int i = 0; i < nCharCount; ++i )
+ {
+ long w = pCharWidths[ i + nCharCount ];
+ w = static_cast<long>(w*fUnitMul + 0.5);
+ pCharWidths[ i ] += w;
+ }
+ }
+
+ long nWidth = 0;
+ for( int i = 0; i < nCharCount; ++i )
+ {
+ nWidth += pCharWidths[ i ] * nFactor;
+ if( nWidth > nMaxWidth )
+ return (i + mnMinCharPos);
+ nWidth += nCharExtra;
+ }
+
+ return STRING_LEN;
+}
+
+// -----------------------------------------------------------------------
+
+long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
+{
+ long nMaxWidth = 0;
+
+ // prepare merging of fallback levels
+ sal_Int32* pTempWidths = NULL;
+ const int nCharCount = mnEndCharPos - mnMinCharPos;
+ if( pCharWidths )
+ {
+ for( int i = 0; i < nCharCount; ++i )
+ pCharWidths[i] = 0;
+ pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
+ }
+
+ for( int n = mnLevel; --n >= 0; )
+ {
+ // query every fallback level
+ long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
+ if( !nTextWidth )
+ continue;
+ // merge results from current level
+ double fUnitMul = mnUnitsPerPixel;
+ fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
+ nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
+ if( nMaxWidth < nTextWidth )
+ nMaxWidth = nTextWidth;
+ if( !pCharWidths )
+ continue;
+ // calculate virtual char widths using most probable fallback layout
+ for( int i = 0; i < nCharCount; ++i )
+ {
+ // #i17359# restriction:
+ // one char cannot be resolved from different fallbacks
+ if( pCharWidths[i] != 0 )
+ continue;
+ long nCharWidth = pTempWidths[i];
+ if( !nCharWidth )
+ continue;
+ nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
+ pCharWidths[i] = nCharWidth;
+ }
+ }
+
+ return nMaxWidth;
+}
+
+// -----------------------------------------------------------------------
+
+void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
+{
+ SalLayout& rLayout = *mpLayouts[ 0 ];
+ rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
+
+ if( mnLevel > 1 )
+ {
+ sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
+ for( int n = 1; n < mnLevel; ++n )
+ {
+ mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
+ double fUnitMul = mnUnitsPerPixel;
+ fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
+ for( int i = 0; i < nMaxIndex; ++i )
+ if( pTempPos[i] >= 0 )
+ {
+ long w = pTempPos[i];
+ w = static_cast<long>(w*fUnitMul + 0.5);
+ pCaretXArray[i] = w;
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
+ int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
+{
+ // for multi-level fallback only single glyphs should be used
+ if( mnLevel > 1 && nLen > 1 )
+ nLen = 1;
+
+ // NOTE: nStart is tagged with current font index
+ int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
+ nStart &= ~GF_FONTMASK;
+ for(; nLevel < mnLevel; ++nLevel, nStart=0 )
+ {
+ SalLayout& rLayout = *mpLayouts[ nLevel ];
+ rLayout.InitFont();
+ int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
+ nStart, pGlyphAdvAry, pCharPosAry );
+ if( nRetVal )
+ {
+ int nFontTag = nLevel << GF_FONTSHIFT;
+ nStart |= nFontTag;
+ double fUnitMul = mnUnitsPerPixel;
+ fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
+ for( int i = 0; i < nRetVal; ++i )
+ {
+ if( pGlyphAdvAry )
+ {
+ long w = pGlyphAdvAry[i];
+ w = static_cast<long>(w * fUnitMul + 0.5);
+ pGlyphAdvAry[i] = w;
+ }
+ pGlyphIdxAry[ i ] |= nFontTag;
+ }
+ rPos += maDrawBase;
+ rPos += maDrawOffset;
+ return nRetVal;
+ }
+ }
+
+ // #111016# reset to base level font when done
+ mpLayouts[0]->InitFont();
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+
+bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
+ ::basegfx::B2DPolyPolygonVector& rPPV ) const
+{
+ bool bRet = false;
+
+ for( int i = mnLevel; --i >= 0; )
+ {
+ SalLayout& rLayout = *mpLayouts[ i ];
+ rLayout.DrawBase() = maDrawBase;
+ rLayout.DrawOffset() += maDrawOffset;
+ rLayout.InitFont();
+ bRet |= rLayout.GetOutline( rGraphics, rPPV );
+ rLayout.DrawOffset() -= maDrawOffset;
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
+{
+ bool bRet = false;
+
+ Rectangle aRectangle;
+ for( int i = mnLevel; --i >= 0; )
+ {
+ SalLayout& rLayout = *mpLayouts[ i ];
+ rLayout.DrawBase() = maDrawBase;
+ rLayout.DrawOffset() += maDrawOffset;
+ rLayout.InitFont();
+ if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
+ {
+ rRect.Union( aRectangle );
+ bRet = true;
+ }
+ rLayout.DrawOffset() -= maDrawOffset;
+ }
+
+ return bRet;
+}
+
+// =======================================================================