summaryrefslogtreecommitdiff
path: root/vcl/win/source/gdi/winlayout.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/win/source/gdi/winlayout.cxx')
-rw-r--r--vcl/win/source/gdi/winlayout.cxx3203
1 files changed, 3203 insertions, 0 deletions
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
new file mode 100644
index 000000000000..bf56b2a1082b
--- /dev/null
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -0,0 +1,3203 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * 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 "tools/svwin.h"
+
+#include "salgdi.h"
+#include "saldata.hxx"
+// for GetMirroredChar
+#include "sft.hxx"
+
+#include "vcl/sallayout.hxx"
+#include "vcl/svapp.hxx"
+
+#include "rtl/ustring.hxx"
+
+#include "osl/module.h"
+#include "osl/file.h"
+
+
+#include <cstdio>
+#include <malloc.h>
+#ifndef __MINGW32__
+#define alloca _alloca
+#endif
+
+#ifdef GCP_KERN_HACK
+ #include <algorithm>
+#endif // GCP_KERN_HACK
+
+
+#define USE_UNISCRIBE
+#ifdef USE_UNISCRIBE
+#include <Usp10.h>
+#include <ShLwApi.h>
+#include <winver.h>
+#endif // USE_UNISCRIBE
+
+#include <hash_map>
+#include <set>
+
+typedef std::hash_map<int,int> IntMap;
+typedef std::set<int> IntSet;
+
+// Graphite headers
+#ifdef ENABLE_GRAPHITE
+#include <i18npool/mslangid.hxx>
+#include <graphite/GrClient.h>
+#include <graphite/WinFont.h>
+#include <graphite/Segment.h>
+#include <vcl/graphite_layout.hxx>
+#include <vcl/graphite_cache.hxx>
+#include <vcl/graphite_features.hxx>
+#endif
+
+#define DROPPED_OUTGLYPH 0xFFFF
+
+using namespace rtl;
+
+// =======================================================================
+
+// win32 specific physical font instance
+class ImplWinFontEntry : public ImplFontEntry
+{
+public:
+ ImplWinFontEntry( ImplFontSelectData& );
+ ~ImplWinFontEntry();
+
+private:
+ // TODO: also add HFONT??? Watch out for issues with too many active fonts...
+
+#ifdef GCP_KERN_HACK
+public:
+ bool HasKernData() const;
+ void SetKernData( int, const KERNINGPAIR* );
+ int GetKerning( sal_Unicode, sal_Unicode ) const;
+private:
+ KERNINGPAIR* mpKerningPairs;
+ int mnKerningPairs;
+#endif // GCP_KERN_HACK
+
+#ifdef USE_UNISCRIBE
+public:
+ SCRIPT_CACHE& GetScriptCache() const
+ { return maScriptCache; }
+private:
+ mutable SCRIPT_CACHE maScriptCache;
+#endif // USE_UNISCRIBE
+
+public:
+ int GetCachedGlyphWidth( int nCharCode ) const;
+ void CacheGlyphWidth( int nCharCode, int nCharWidth );
+
+ bool InitKashidaHandling( HDC );
+ int GetMinKashidaWidth() const { return mnMinKashidaWidth; }
+ int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
+
+private:
+ IntMap maWidthMap;
+ mutable int mnMinKashidaWidth;
+ mutable int mnMinKashidaGlyph;
+};
+
+// -----------------------------------------------------------------------
+
+inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
+{
+ maWidthMap[ nCharCode ] = nCharWidth;
+}
+
+inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
+{
+ IntMap::const_iterator it = maWidthMap.find( nCharCode );
+ if( it == maWidthMap.end() )
+ return -1;
+ return it->second;
+}
+
+// =======================================================================
+
+class WinLayout : public SalLayout
+{
+public:
+ WinLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
+ virtual void InitFont() const;
+ void SetFontScale( float f ) { mfFontScale = f; }
+ float GetFontScale() const { return mfFontScale; }
+ HFONT DisableFontScaling( void) const;
+
+#ifdef USE_UNISCRIBE
+ SCRIPT_CACHE& GetScriptCache() const
+ { return mrWinFontEntry.GetScriptCache(); }
+#endif // USE_UNISCRIBE
+
+protected:
+ HDC mhDC; // WIN32 device handle
+ HFONT mhFont; // WIN32 font handle
+ int mnBaseAdv; // x-offset relative to Layout origin
+ float mfFontScale; // allows metrics emulation of huge font sizes
+
+ const ImplWinFontData& mrWinFontData;
+ ImplWinFontEntry& mrWinFontEntry;
+};
+
+// =======================================================================
+
+class SimpleWinLayout : public WinLayout
+{
+public:
+ SimpleWinLayout( HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry& );
+ virtual ~SimpleWinLayout();
+
+ virtual bool LayoutText( ImplLayoutArgs& );
+ virtual void AdjustLayout( ImplLayoutArgs& );
+ virtual void DrawText( SalGraphics& ) const;
+
+ virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
+ sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
+
+ virtual long FillDXArray( long* pDXArray ) const;
+ virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
+
+ // for glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos );
+ virtual void DropGlyph( int nStart );
+ virtual void Simplify( bool bIsBase );
+
+protected:
+ void Justify( long nNewWidth );
+ void ApplyDXArray( const ImplLayoutArgs& );
+
+private:
+ int mnGlyphCount;
+ int mnCharCount;
+ WCHAR* mpOutGlyphs;
+ int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[]
+ int* mpGlyphOrigAdvs;
+ int* mpCharWidths; // map rel char pos to char width
+ int* mpChars2Glyphs; // map rel char pos to abs glyph pos
+ int* mpGlyphs2Chars; // map abs glyph pos to abs char pos
+ bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL
+ mutable long mnWidth;
+ bool mbDisableGlyphs;
+
+ int mnNotdefWidth;
+ BYTE mnCharSet;
+};
+
+// =======================================================================
+
+WinLayout::WinLayout( HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE )
+: mhDC( hDC ),
+ mhFont( (HFONT)::GetCurrentObject(hDC,OBJ_FONT) ),
+ mnBaseAdv( 0 ),
+ mfFontScale( 1.0 ),
+ mrWinFontData( rWFD ),
+ mrWinFontEntry( rWFE )
+{}
+
+// -----------------------------------------------------------------------
+
+void WinLayout::InitFont() const
+{
+ ::SelectObject( mhDC, mhFont );
+}
+
+// -----------------------------------------------------------------------
+
+// Using reasonably sized fonts to emulate huge fonts works around
+// a lot of problems in printer and display drivers. Huge fonts are
+// mostly used by high resolution reference devices which are never
+// painted to anyway. In the rare case that a huge font needs to be
+// displayed somewhere then the workaround doesn't help anymore.
+// If the drivers fail silently for huge fonts, so be it...
+HFONT WinLayout::DisableFontScaling() const
+{
+ if( mfFontScale == 1.0 )
+ return 0;
+
+ HFONT hHugeFont = 0;
+ if( aSalShlData.mbWNT )
+ {
+ LOGFONTW aLogFont;
+ ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
+ aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
+ aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
+ hHugeFont = ::CreateFontIndirectW( &aLogFont);
+ }
+ else
+ {
+ LOGFONTA aLogFont;
+ ::GetObjectA( mhFont, sizeof(LOGFONTA), &aLogFont);
+ aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
+ aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
+ hHugeFont = ::CreateFontIndirectA( &aLogFont);
+ }
+
+ if( !hHugeFont )
+ return 0;
+
+ return SelectFont( mhDC, hHugeFont );
+}
+
+// =======================================================================
+
+SimpleWinLayout::SimpleWinLayout( HDC hDC, BYTE nCharSet,
+ const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
+: WinLayout( hDC, rWinFontData, rWinFontEntry ),
+ mnGlyphCount( 0 ),
+ mnCharCount( 0 ),
+ mpOutGlyphs( NULL ),
+ mpGlyphAdvances( NULL ),
+ mpGlyphOrigAdvs( NULL ),
+ mpCharWidths( NULL ),
+ mpChars2Glyphs( NULL ),
+ mpGlyphs2Chars( NULL ),
+ mpGlyphRTLFlags( NULL ),
+ mnWidth( 0 ),
+ mnNotdefWidth( -1 ),
+ mnCharSet( nCharSet ),
+ mbDisableGlyphs( false )
+{
+ mbDisableGlyphs = true;
+}
+
+// -----------------------------------------------------------------------
+
+SimpleWinLayout::~SimpleWinLayout()
+{
+ delete[] mpGlyphRTLFlags;
+ delete[] mpGlyphs2Chars;
+ delete[] mpChars2Glyphs;
+ if( mpCharWidths != mpGlyphAdvances )
+ delete[] mpCharWidths;
+ delete[] mpGlyphOrigAdvs;
+ delete[] mpGlyphAdvances;
+ delete[] mpOutGlyphs;
+}
+
+// -----------------------------------------------------------------------
+
+bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ // prepare layout
+ // TODO: fix case when recyclying old SimpleWinLayout object
+ mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0);
+ mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+
+ if( !mbDisableGlyphs )
+ {
+ // Win32 glyph APIs have serious problems with vertical layout
+ // => workaround is to use the unicode methods then
+ if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL )
+ mbDisableGlyphs = true;
+ else
+ // use cached value from font face
+ mbDisableGlyphs = mrWinFontData.IsGlyphApiDisabled();
+ }
+
+ // TODO: use a cached value for bDisableAsianKern from upper layers
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
+ {
+ TEXTMETRICA aTextMetricA;
+ if( ::GetTextMetricsA( mhDC, &aTextMetricA )
+ && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
+ rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN;
+ }
+
+ // layout text
+ int i, j;
+
+ mnGlyphCount = 0;
+ bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0;
+
+ // count the number of chars to process if no RTL run
+ rArgs.ResetPos();
+ bool bHasRTL = false;
+ while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
+ mnGlyphCount += j - i;
+
+ // if there are RTL runs we need room to remember individual BiDi flags
+ if( bHasRTL )
+ {
+ mpGlyphRTLFlags = new bool[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpGlyphRTLFlags[i] = false;
+ }
+
+ // rewrite the logical string if needed to prepare for the API calls
+ const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos;
+ if( (mnGlyphCount != mnCharCount) || bVertical )
+ {
+ // we need to rewrite the pBidiStr when any of
+ // - BiDirectional layout
+ // - vertical layout
+ // - partial runs (e.g. with control chars or for glyph fallback)
+ // are involved
+ sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
+ pBidiStr = pRewrittenStr;
+
+ // note: glyph to char mapping is relative to first character
+ mpChars2Glyphs = new int[ mnCharCount ];
+ mpGlyphs2Chars = new int[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
+
+ mnGlyphCount = 0;
+ rArgs.ResetPos();
+ bool bIsRTL = false;
+ while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
+ {
+ do
+ {
+ // get the next leftmost character in this run
+ int nCharPos = bIsRTL ? --j : i++;
+ sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
+
+ // in the RTL case mirror the character and remember its RTL status
+ if( bIsRTL )
+ {
+ cChar = ::GetMirroredChar( cChar );
+ mpGlyphRTLFlags[ mnGlyphCount ] = true;
+ }
+
+ // for vertical writing use vertical alternatives
+ if( bVertical )
+ {
+ sal_UCS4 cVert = ::GetVerticalChar( cChar );
+ if( cVert )
+ cChar = cVert;
+ }
+
+ // rewrite the original string
+ // update the mappings between original and rewritten string
+ // TODO: support surrogates in rewritten strings
+ pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
+ mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
+ mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
+ ++mnGlyphCount;
+ } while( i < j );
+ }
+ }
+
+ mpOutGlyphs = new WCHAR[ mnGlyphCount ];
+ mpGlyphAdvances = new int[ mnGlyphCount ];
+
+ if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) )
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+
+#ifndef GCP_KERN_HACK
+ DWORD nGcpOption = 0;
+ // enable kerning if requested
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
+ nGcpOption |= GCP_USEKERNING;
+#endif // GCP_KERN_HACK
+
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpOutGlyphs[i] = pBidiStr[ i ];
+ mnWidth = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ // get the current UCS-4 code point, check for surrogate pairs
+ const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
+ unsigned nCharCode = pCodes[0];
+ bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
+ if( bSurrogate )
+ {
+ if( nCharCode >= 0xDC00 ) // this part of a surrogate pair was already processed
+ continue;
+ nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
+ }
+
+ // get the advance width for the current UCS-4 code point
+ int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
+ if( nGlyphWidth == -1 )
+ {
+ ABC aABC;
+ SIZE aExtent;
+ if( ::GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
+ nGlyphWidth = aExtent.cx;
+ else if( ::GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
+ nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
+ else if( !::GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
+ && !::GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
+ nGlyphWidth = 0;
+ mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
+ }
+ mpGlyphAdvances[ i ] = nGlyphWidth;
+ mnWidth += nGlyphWidth;
+
+ // remaining codes of surrogate pair get a zero width
+ if( bSurrogate && ((i+1) < mnGlyphCount) )
+ mpGlyphAdvances[ i+1 ] = 0;
+
+ // check with the font face if glyph fallback is needed
+ if( mrWinFontData.HasChar( nCharCode ) )
+ continue;
+
+ // request glyph fallback at this position in the string
+ bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
+ int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
+ rArgs.NeedFallback( nCharPos, bRTL );
+ if( bSurrogate && ((nCharPos+1) < rArgs.mnLength) )
+ rArgs.NeedFallback( nCharPos+1, bRTL );
+
+ // replace the current glyph shape with the NotDef glyph shape
+ if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
+ {
+ // when we already are layouting for glyph fallback
+ // then a new unresolved glyph is not interesting
+ mnNotdefWidth = 0;
+ mpOutGlyphs[i] = DROPPED_OUTGLYPH;
+ }
+ else
+ {
+ if( mnNotdefWidth < 0 )
+ {
+ // get the width of the NotDef glyph
+ SIZE aExtent;
+ WCHAR cNotDef = rArgs.mpStr[ nCharPos ];
+ mnNotdefWidth = 0;
+ if( ::GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
+ mnNotdefWidth = aExtent.cx;
+ }
+ // use a better NotDef glyph
+ if( !mbDisableGlyphs && !bSurrogate )
+ mpOutGlyphs[i] = 0;
+ }
+ if( bSurrogate && ((i+1) < mnGlyphCount) )
+ mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
+
+ // adjust the current glyph width to the NotDef glyph width
+ mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
+ mpGlyphAdvances[i] = mnNotdefWidth;
+ if( mpGlyphOrigAdvs )
+ mpGlyphOrigAdvs[i] = mnNotdefWidth;
+ }
+
+#ifdef GCP_KERN_HACK
+ // apply kerning if the layout engine has not yet done it
+ if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) )
+ {
+#else // GCP_KERN_HACK
+ // apply just asian kerning
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
+ {
+ if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) )
+#endif // GCP_KERN_HACK
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
+
+ // #99658# also apply asian kerning on the substring border
+ int nLen = mnGlyphCount;
+ if( rArgs.mnMinCharPos + nLen < rArgs.mnLength )
+ ++nLen;
+ for( i = 1; i < nLen; ++i )
+ {
+#ifdef GCP_KERN_HACK
+ if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
+ {
+ int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
+ mpGlyphAdvances[ i-1 ] += nKernAmount;
+ mnWidth += nKernAmount;
+ }
+ else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
+#endif // GCP_KERN_HACK
+
+ if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
+ && ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
+ {
+ long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
+ long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical );
+
+ long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
+ if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
+ {
+ nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
+ mpGlyphAdvances[i-1] += nDelta;
+ mnWidth += nDelta;
+ }
+ }
+ }
+ }
+
+ // calculate virtual char widths
+ if( !mpGlyphs2Chars )
+ mpCharWidths = mpGlyphAdvances;
+ else
+ {
+ mpCharWidths = new int[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[ i ] = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+ if( j >= 0 )
+ mpCharWidths[ j ] += mpGlyphAdvances[ i ];
+ }
+ }
+
+ // scale layout metrics if needed
+ // TODO: does it make the code more simple if the metric scaling
+ // is moved to the methods that need metric scaling (e.g. FillDXArray())?
+ if( mfFontScale != 1.0 )
+ {
+ mnWidth = (long)(mnWidth * mfFontScale);
+ mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
+ if( mpGlyphAdvances != mpCharWidths )
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
+ if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int& nStart,
+ long* pGlyphAdvances, int* pCharIndexes ) const
+{
+ // return zero if no more glyph found
+ if( nStart >= mnGlyphCount )
+ return 0;
+
+ // calculate glyph position relative to layout base
+ // TODO: avoid for nStart!=0 case by reusing rPos
+ long nXOffset = mnBaseAdv;
+ for( int i = 0; i < nStart; ++i )
+ nXOffset += mpGlyphAdvances[ i ];
+
+ // calculate absolute position in pixel units
+ Point aRelativePos( nXOffset, 0 );
+ rPos = GetDrawPosition( aRelativePos );
+
+ int nCount = 0;
+ while( nCount < nLen )
+ {
+ // update return values {nGlyphIndex,nCharPos,nGlyphAdvance}
+ sal_GlyphId nGlyphIndex = mpOutGlyphs[ nStart ];
+ if( mbDisableGlyphs )
+ {
+ if( mnLayoutFlags & SAL_LAYOUT_VERTICAL )
+ {
+ const sal_UCS4 cChar = static_cast<sal_UCS4>(nGlyphIndex & GF_IDXMASK);
+ if( mrWinFontData.HasGSUBstitutions( mhDC )
+ && mrWinFontData.IsGSUBstituted( cChar ) )
+ nGlyphIndex |= GF_GSUB | GF_ROTL;
+ else
+ {
+ nGlyphIndex |= GetVerticalFlags( cChar );
+ if( (nGlyphIndex & GF_ROTMASK) == 0 )
+ nGlyphIndex |= GF_VERT;
+ }
+ }
+ nGlyphIndex |= GF_ISCHAR;
+ }
+ ++nCount;
+ *(pGlyphs++) = nGlyphIndex;
+ if( pGlyphAdvances )
+ *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
+ if( pCharIndexes )
+ {
+ int nCharPos;
+ if( !mpGlyphs2Chars )
+ nCharPos = nStart + mnMinCharPos;
+ else
+ nCharPos = mpGlyphs2Chars[nStart];
+ *(pCharIndexes++) = nCharPos;
+ }
+
+ // stop at last glyph
+ if( ++nStart >= mnGlyphCount )
+ break;
+
+ // stop when next x-position is unexpected
+ if( !pGlyphAdvances && mpGlyphOrigAdvs )
+ if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
+ break;
+ }
+
+ return nCount;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::DrawText( SalGraphics& rGraphics ) const
+{
+ if( mnGlyphCount <= 0 )
+ return;
+
+ WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
+ HDC aHDC = rWinGraphics.mhDC;
+
+ HFONT hOrigFont = DisableFontScaling();
+
+ UINT mnDrawOptions = ETO_GLYPH_INDEX;
+ if( mbDisableGlyphs )
+ mnDrawOptions = 0;
+
+ Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
+
+ // #108267#, limit the number of glyphs to avoid paint errors
+ UINT limitedGlyphCount = Min( 8192, mnGlyphCount );
+ if( mnDrawOptions || aSalShlData.mbWNT )
+ {
+ // #108267#, break up into glyph portions of a limited size required by Win32 API
+ const unsigned int maxGlyphCount = 8192;
+ UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
+ UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
+
+ if( numGlyphPortions )
+ {
+ // #108267#,#109387# break up string into smaller chunks
+ // the output positions will be updated by windows (SetTextAlign)
+ unsigned int i,n;
+ POINT oldPos;
+ UINT oldTa = ::GetTextAlign( aHDC );
+ ::SetTextAlign( aHDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP );
+ ::MoveToEx( aHDC, aPos.X(), aPos.Y(), &oldPos );
+ for( i=n=0; n<numGlyphPortions; n++, i+=maxGlyphCount )
+ ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
+ mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i );
+ ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
+ mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i );
+ ::MoveToEx( aHDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
+ ::SetTextAlign( aHDC, oldTa );
+ }
+ else
+ ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), mnDrawOptions, NULL,
+ mpOutGlyphs, mnGlyphCount, mpGlyphAdvances );
+ }
+ else
+ {
+ // #108267#, On Win9x, we get paint errors when drawing huge strings, even when
+ // split into pieces (see above), seems to be a problem in the internal text clipping
+ // so we just cut off the string
+ if( !mpGlyphOrigAdvs )
+ ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), 0, NULL,
+ mpOutGlyphs, limitedGlyphCount, NULL );
+ else
+ {
+ // workaround for problem in #106259#
+ long nXPos = mnBaseAdv;
+ for( unsigned int i = 0; i < limitedGlyphCount; ++i )
+ {
+ ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), 0, NULL,
+ mpOutGlyphs+i, 1, NULL );
+ nXPos += mpGlyphAdvances[ i ];
+ aPos = GetDrawPosition( Point( nXPos, 0 ) );
+ }
+ }
+ }
+
+ if( hOrigFont )
+ DeleteFont( SelectFont( aHDC, hOrigFont ) );
+}
+
+// -----------------------------------------------------------------------
+
+long SimpleWinLayout::FillDXArray( long* pDXArray ) const
+{
+ if( !mnWidth )
+ {
+ long mnWidth = mnBaseAdv;
+ for( int i = 0; i < mnGlyphCount; ++i )
+ mnWidth += mpGlyphAdvances[ i ];
+ }
+
+ if( pDXArray != NULL )
+ {
+ for( int i = 0; i < mnCharCount; ++i )
+ pDXArray[ i ] = mpCharWidths[ i ];
+ }
+
+ return mnWidth;
+}
+
+// -----------------------------------------------------------------------
+
+int SimpleWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+// NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
+{
+ if( mnWidth )
+ if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
+ return STRING_LEN;
+
+ long nExtraWidth = mnBaseAdv * nFactor;
+ for( int n = 0; n < mnCharCount; ++n )
+ {
+ // skip unused characters
+ if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
+ continue;
+ // add char widths until max
+ nExtraWidth += mpCharWidths[ n ] * nFactor;
+ if( nExtraWidth >= nMaxWidth )
+ return (mnMinCharPos + n);
+ nExtraWidth += nCharExtra;
+ }
+
+ return STRING_LEN;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
+{
+ long nXPos = mnBaseAdv;
+
+ if( !mpGlyphs2Chars )
+ {
+ for( int i = 0; i < nMaxIdx; i += 2 )
+ {
+ pCaretXArray[ i ] = nXPos;
+ nXPos += mpGlyphAdvances[ i>>1 ];
+ pCaretXArray[ i+1 ] = nXPos;
+ }
+ }
+ else
+ {
+ int i;
+ for( i = 0; i < nMaxIdx; ++i )
+ pCaretXArray[ i ] = -1;
+
+ // assign glyph positions to character positions
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
+ nCurrIdx *= 2;
+ if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
+ {
+ // 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;
+ }
+ nXPos += mpGlyphAdvances[ i ];
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::Justify( long nNewWidth )
+{
+ long nOldWidth = mnWidth;
+ mnWidth = nNewWidth;
+
+ if( mnGlyphCount <= 0 )
+ return;
+
+ if( nNewWidth == nOldWidth )
+ return;
+
+ // the rightmost glyph cannot be stretched
+ const int nRight = mnGlyphCount - 1;
+ nOldWidth -= mpGlyphAdvances[ nRight ];
+ nNewWidth -= mpGlyphAdvances[ nRight ];
+
+ // count stretchable glyphs
+ int nStretchable = 0, i;
+ for( i = 0; i < nRight; ++i )
+ if( mpGlyphAdvances[i] >= 0 )
+ ++nStretchable;
+
+ // stretch these glyphs
+ int nDiffWidth = nNewWidth - nOldWidth;
+ for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
+ {
+ if( mpGlyphAdvances[i] <= 0 )
+ continue;
+ int nDeltaWidth = nDiffWidth / nStretchable;
+ mpGlyphAdvances[i] += nDeltaWidth;
+ --nStretchable;
+ nDiffWidth -= nDeltaWidth;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+
+ // adjust positions if requested
+ if( rArgs.mpDXArray )
+ ApplyDXArray( rArgs );
+ else if( rArgs.mnLayoutWidth )
+ Justify( rArgs.mnLayoutWidth );
+ else
+ return;
+
+ // recalculate virtual char widths if they were changed
+ if( mpCharWidths != mpGlyphAdvances )
+ {
+ int i;
+ if( !mpGlyphs2Chars )
+ {
+ // standard LTR case
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpCharWidths[ i ] = mpGlyphAdvances[ i ];
+ }
+ else
+ {
+ // BiDi or complex case
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[ i ] = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+ if( j >= 0 )
+ mpCharWidths[ j ] += mpGlyphAdvances[ i ];
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
+{
+ // try to avoid disturbance of text flow for LSB rounding case;
+ const long* pDXArray = rArgs.mpDXArray;
+
+ int i = 0;
+ long nOldWidth = mnBaseAdv;
+ for(; i < mnCharCount; ++i )
+ {
+ int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+ if( j >= 0 )
+ {
+ nOldWidth += mpGlyphAdvances[ j ];
+ int nDiff = nOldWidth - pDXArray[ i ];
+
+ // disabled because of #104768#
+ // works great for static text, but problems when typing
+ // if( nDiff>+1 || nDiff<-1 )
+ // only bother with changing anything when something moved
+ if( nDiff != 0 )
+ break;
+ }
+ }
+ if( i >= mnCharCount )
+ return;
+
+ if( !mpGlyphOrigAdvs )
+ {
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
+ }
+
+ mnWidth = mnBaseAdv;
+ for( i = 0; i < mnCharCount; ++i )
+ {
+ int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+ if( j >= 0 )
+ mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
+ mnWidth = pDXArray[i];
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
+{
+ if( nStart > mnGlyphCount )
+ return;
+
+ // calculate the current x-position of the requested glyph
+ // TODO: cache absolute positions
+ int nXPos = mnBaseAdv;
+ for( int i = 0; i < nStart; ++i )
+ nXPos += mpGlyphAdvances[i];
+
+ // calculate the difference to the current glyph position
+ int nDelta = nNewXPos - nXPos;
+
+ // adjust the width of the layout if it was already cached
+ if( mnWidth )
+ mnWidth += nDelta;
+
+ // depending on whether the requested glyph is leftmost in the layout
+ // adjust either the layout's or the requested glyph's relative position
+ if( nStart > 0 )
+ mpGlyphAdvances[ nStart-1 ] += nDelta;
+ else
+ mnBaseAdv += nDelta;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::DropGlyph( int nStart )
+{
+ mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
+}
+
+// -----------------------------------------------------------------------
+
+void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
+{
+ // return early if no glyph has been dropped
+ int i = mnGlyphCount;
+ while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
+ if( i < 0 )
+ return;
+
+ // convert the layout to a sparse layout if it is not already
+ if( !mpGlyphs2Chars )
+ {
+ mpGlyphs2Chars = new int[ mnGlyphCount ];
+ mpCharWidths = new int[ mnCharCount ];
+ // assertion: mnGlyphCount == mnCharCount
+ for( int k = 0; k < mnGlyphCount; ++k )
+ {
+ mpGlyphs2Chars[ k ] = mnMinCharPos + k;
+ mpCharWidths[ k ] = mpGlyphAdvances[ k ];
+ }
+ }
+
+ // remove dropped glyphs that are rightmost in the layout
+ for( i = mnGlyphCount; --i >= 0; )
+ {
+ if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
+ break;
+ if( mnWidth )
+ mnWidth -= mpGlyphAdvances[ i ];
+ int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ if( nRelCharPos >= 0 )
+ mpCharWidths[ nRelCharPos ] = 0;
+ }
+ mnGlyphCount = i + 1;
+
+ // keep original glyph widths around
+ if( !mpGlyphOrigAdvs )
+ {
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+ for( int k = 0; k < mnGlyphCount; ++k )
+ mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
+ }
+
+ // remove dropped glyphs inside the layout
+ int nNewGC = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
+ {
+ // adjust relative position to last valid glyph
+ int nDroppedWidth = mpGlyphAdvances[ i ];
+ mpGlyphAdvances[ i ] = 0;
+ if( nNewGC > 0 )
+ mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
+ else
+ mnBaseAdv += nDroppedWidth;
+
+ // zero the virtual char width for the char that has a fallback
+ int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ if( nRelCharPos >= 0 )
+ mpCharWidths[ nRelCharPos ] = 0;
+ }
+ else
+ {
+ if( nNewGC != i )
+ {
+ // rearrange the glyph array to get rid of the dropped glyph
+ mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ];
+ mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
+ mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
+ mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ];
+ }
+ ++nNewGC;
+ }
+ }
+
+ mnGlyphCount = nNewGC;
+ if( mnGlyphCount <= 0 )
+ mnWidth = mnBaseAdv = 0;
+}
+
+// =======================================================================
+
+#ifdef USE_UNISCRIBE
+
+struct VisualItem
+{
+public:
+ SCRIPT_ITEM* mpScriptItem;
+ int mnMinGlyphPos;
+ int mnEndGlyphPos;
+ int mnMinCharPos;
+ int mnEndCharPos;
+ //long mnPixelWidth;
+ int mnXOffset;
+ ABC maABCWidths;
+ bool mbHasKashidas;
+
+public:
+ bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
+ bool IsRTL() const { return mpScriptItem->a.fRTL; }
+ bool HasKashidas() const { return mbHasKashidas; }
+};
+
+// -----------------------------------------------------------------------
+
+class UniscribeLayout : public WinLayout
+{
+public:
+ UniscribeLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
+
+ virtual bool LayoutText( ImplLayoutArgs& );
+ virtual void AdjustLayout( ImplLayoutArgs& );
+ virtual void DrawText( SalGraphics& ) const;
+ virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
+ sal_Int32* pGlyphAdvances, int* pCharPosAry ) const;
+
+ virtual long FillDXArray( long* pDXArray ) const;
+ virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
+ virtual bool IsKashidaPosValid ( int nCharPos ) const;
+
+ // for glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos );
+ virtual void DropGlyph( int nStart );
+ virtual void Simplify( bool bIsBase );
+ virtual void DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; }
+
+protected:
+ virtual ~UniscribeLayout();
+
+ void Justify( long nNewWidth );
+ void ApplyDXArray( const ImplLayoutArgs& );
+
+ bool GetItemSubrange( const VisualItem&,
+ int& rMinIndex, int& rEndIndex ) const;
+
+private:
+ // item specific info
+ SCRIPT_ITEM* mpScriptItems; // in logical order
+ VisualItem* mpVisualItems; // in visual order
+ int mnItemCount; // number of visual items
+
+ // string specific info
+ // everything is in logical order
+ int mnCharCapacity;
+ WORD* mpLogClusters; // map from absolute_char_pos to relative_glyph_pos
+ int* mpCharWidths; // map from absolute_char_pos to char_width
+ int mnSubStringMin; // char_pos of first char in context
+
+ // glyph specific info
+ // everything is in visual order
+ int mnGlyphCount;
+ int mnGlyphCapacity;
+ int* mpGlyphAdvances; // glyph advance width before justification
+ int* mpJustifications; // glyph advance width after justification
+ WORD* mpOutGlyphs; // glyphids in visual order
+ GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout
+ SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes
+ mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos
+
+ // kashida stuff
+ void InitKashidaHandling();
+ void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
+ bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
+
+ int mnMinKashidaWidth;
+ int mnMinKashidaGlyph;
+ bool mbDisableGlyphInjection;
+};
+
+// -----------------------------------------------------------------------
+// dynamic loading of usp library
+
+static oslModule aUspModule = NULL;
+static bool bUspEnabled = true;
+
+static HRESULT ((WINAPI *pScriptIsComplex)( const WCHAR*, int, DWORD ));
+static HRESULT ((WINAPI *pScriptItemize)( const WCHAR*, int, int,
+ const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int* ));
+static HRESULT ((WINAPI *pScriptShape)( HDC, SCRIPT_CACHE*, const WCHAR*,
+ int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int* ));
+static HRESULT ((WINAPI *pScriptPlace)( HDC, SCRIPT_CACHE*, const WORD*, int,
+ const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC* ));
+static HRESULT ((WINAPI *pScriptGetLogicalWidths)( const SCRIPT_ANALYSIS*,
+ int, int, const int*, const WORD*, const SCRIPT_VISATTR*, int* ));
+static HRESULT ((WINAPI *pScriptApplyLogicalWidth)( const int*, int, int, const WORD*,
+ const SCRIPT_VISATTR*, const int*, const SCRIPT_ANALYSIS*, ABC*, int* ));
+static HRESULT ((WINAPI *pScriptJustify)( const SCRIPT_VISATTR*,
+ const int*, int, int, int, int* ));
+static HRESULT ((WINAPI *pScriptTextOut)( const HDC, SCRIPT_CACHE*,
+ int, int, UINT, const RECT*, const SCRIPT_ANALYSIS*, const WCHAR*,
+ int, const WORD*, int, const int*, const int*, const GOFFSET* ));
+static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* ));
+static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* ));
+
+static bool bManualCellAlign = true;
+
+// -----------------------------------------------------------------------
+
+static bool InitUSP()
+{
+ OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) );
+ aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
+ if( !aUspModule )
+ return (bUspEnabled = false);
+
+ pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" );
+ bUspEnabled &= (NULL != pScriptIsComplex);
+
+ pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int,
+ const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" );
+ bUspEnabled &= (NULL != pScriptItemize);
+
+ pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*,
+ int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" );
+ bUspEnabled &= (NULL != pScriptShape);
+
+ pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int,
+ const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" );
+ bUspEnabled &= (NULL != pScriptPlace);
+
+ pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*,
+ int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" );
+ bUspEnabled &= (NULL != pScriptGetLogicalWidths);
+
+ pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*,
+ const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" );
+ bUspEnabled &= (NULL != pScriptApplyLogicalWidth);
+
+ pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*,
+ int,int,int,int*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" );
+ bUspEnabled &= (NULL != pScriptJustify);
+
+ pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" );
+ bUspEnabled &= (NULL != pScriptGetFontProperties);
+
+ pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*,
+ int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*,
+ int,const WORD*,int,const int*,const int*,const GOFFSET*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" );
+ bUspEnabled &= (NULL != pScriptTextOut);
+
+ pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*))
+ osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" );
+ bUspEnabled &= (NULL != pScriptFreeCache);
+
+ if( !bUspEnabled )
+ {
+ osl_unloadModule( aUspModule );
+ aUspModule = NULL;
+ }
+
+ // get the DLL version info
+ int nUspVersion = 0;
+ // TODO: there must be a simpler way to get the friggin version info from OSL?
+ rtl_uString* pModuleURL = NULL;
+ osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL );
+ rtl_uString* pModuleFileName = NULL;
+ if( pModuleURL )
+ osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
+ const sal_Unicode* pModuleFileCStr = NULL;
+ if( pModuleFileName )
+ pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
+ if( pModuleFileCStr )
+ {
+ DWORD nHandle;
+ DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
+ char* pBuffer = (char*)alloca( nBufSize );
+ WIN_BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
+ VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
+ UINT nFixedFileSize = 0;
+ if( bRC )
+ ::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
+ if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
+ nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
+ + LOWORD(pFixedFileInfo->dwProductVersionMS);
+ }
+
+ // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
+ if( nUspVersion >= 10600 )
+ bManualCellAlign = false;
+
+ return bUspEnabled;
+}
+
+// -----------------------------------------------------------------------
+
+UniscribeLayout::UniscribeLayout( HDC hDC,
+ const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
+: WinLayout( hDC, rWinFontData, rWinFontEntry ),
+ mnItemCount( 0 ),
+ mpScriptItems( NULL ),
+ mpVisualItems( NULL ),
+ mpLogClusters( NULL ),
+ mpCharWidths( NULL ),
+ mnCharCapacity( 0 ),
+ mnSubStringMin( 0 ),
+ mnGlyphCapacity( 0 ),
+ mnGlyphCount( 0 ),
+ mpOutGlyphs( NULL ),
+ mpGlyphAdvances( NULL ),
+ mpJustifications( NULL ),
+ mpGlyphOffsets( NULL ),
+ mpVisualAttrs( NULL ),
+ mpGlyphs2Chars( NULL ),
+ mnMinKashidaGlyph( 0 ),
+ mbDisableGlyphInjection( false )
+{}
+
+// -----------------------------------------------------------------------
+
+UniscribeLayout::~UniscribeLayout()
+{
+ delete[] mpScriptItems;
+ delete[] mpVisualItems;
+ delete[] mpLogClusters;
+ delete[] mpCharWidths;
+ delete[] mpOutGlyphs;
+ delete[] mpGlyphAdvances;
+ delete[] mpJustifications;
+ delete[] mpGlyphOffsets;
+ delete[] mpVisualAttrs;
+ delete[] mpGlyphs2Chars;
+}
+
+// -----------------------------------------------------------------------
+
+bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ // for a base layout only the context glyphs have to be dropped
+ // => when the whole string is involved there is no extra context
+ typedef std::vector<int> TIntVector;
+ TIntVector aDropChars;
+ if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
+ {
+ // calculate superfluous context char positions
+ aDropChars.push_back( 0 );
+ aDropChars.push_back( rArgs.mnLength );
+ int nMin, nEnd;
+ bool bRTL;
+ for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
+ {
+ aDropChars.push_back( nMin );
+ aDropChars.push_back( nEnd );
+ }
+ // prepare aDropChars for binary search which will allow to
+ // not bother with visual items that will be dropped anyway
+ std::sort( aDropChars.begin(), aDropChars.end() );
+ }
+
+ // prepare layout
+ // TODO: fix case when recyclying old UniscribeLayout object
+ mnMinCharPos = rArgs.mnMinCharPos;
+ mnEndCharPos = rArgs.mnEndCharPos;
+
+ // determine script items from string
+
+ // prepare itemization
+ // TODO: try to avoid itemization since it costs a lot of performance
+ SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0};
+ aScriptState.uBidiLevel = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL));
+ aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG));
+ aScriptState.fDigitSubstitute = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
+ aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
+ DWORD nLangId = 0; // TODO: get language from font
+ SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0};
+ aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
+ aScriptControl.fContextDigits = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
+ // determine relevant substring and work only on it
+ // when Bidi status is unknown we need to look at the whole string though
+ mnSubStringMin = 0;
+ int nSubStringEnd = rArgs.mnLength;
+ if( aScriptState.fOverrideDirection )
+ {
+ // TODO: limit substring to portion limits
+ mnSubStringMin = rArgs.mnMinCharPos - 8;
+ if( mnSubStringMin < 0 )
+ mnSubStringMin = 0;
+ nSubStringEnd = rArgs.mnEndCharPos + 8;
+ if( nSubStringEnd > rArgs.mnLength )
+ nSubStringEnd = rArgs.mnLength;
+
+ }
+
+ // now itemize the substring with its context
+ for( int nItemCapacity = 16;; nItemCapacity *= 8 )
+ {
+ mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
+ HRESULT nRC = (*pScriptItemize)(
+ reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
+ nItemCapacity - 1, &aScriptControl, &aScriptState,
+ mpScriptItems, &mnItemCount );
+ if( !nRC ) // break loop when everything is correctly itemized
+ break;
+
+ // prepare bigger buffers for another itemization round
+ delete[] mpScriptItems;
+ mpScriptItems = NULL;
+ if( nRC != E_OUTOFMEMORY )
+ return false;
+ if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 )
+ return false;
+ }
+
+ // calculate the order of visual items
+ int nItem, i;
+
+ // adjust char positions by substring offset
+ for( nItem = 0; nItem <= mnItemCount; ++nItem )
+ mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
+ // default visual item ordering
+ mpVisualItems = new VisualItem[ mnItemCount ];
+ for( nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ // initialize char specific item info
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
+ rVisualItem.mpScriptItem = pScriptItem;
+ rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
+ rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
+ }
+
+ // reorder visual item order if needed
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
+ {
+ // force RTL item ordering if requested
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL )
+ {
+ VisualItem* pVI0 = &mpVisualItems[ 0 ];
+ VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
+ while( pVI0 < --pVI1 )
+ {
+ VisualItem aVtmp = *pVI0;
+ *(pVI0++) = *pVI1;
+ *pVI1 = aVtmp;
+ }
+ }
+ }
+ else if( mnItemCount > 1 )
+ {
+ // apply bidi algorithm's rule L2 on item level
+ // TODO: use faster L2 algorithm
+ int nMaxBidiLevel = 0;
+ VisualItem* pVI = &mpVisualItems[0];
+ VisualItem* const pVIend = pVI + mnItemCount;
+ for(; pVI < pVIend; ++pVI )
+ if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
+ nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
+
+ while( --nMaxBidiLevel >= 0 )
+ {
+ for( pVI = &mpVisualItems[0]; pVI < pVIend; )
+ {
+ // find item range that needs reordering
+ for(; pVI < pVIend; ++pVI )
+ if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
+ break;
+ VisualItem* pVImin = pVI++;
+ for(; pVI < pVIend; ++pVI )
+ if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
+ break;
+ VisualItem* pVImax = pVI++;
+
+ // reverse order of items in this range
+ while( pVImin < --pVImax )
+ {
+ VisualItem aVtmp = *pVImin;
+ *(pVImin++) = *pVImax;
+ *pVImax = aVtmp;
+ }
+ }
+ }
+ }
+
+ // allocate arrays
+ // TODO: when reusing object reuse old allocations or delete them
+ // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
+ mnCharCapacity = nSubStringEnd;
+ mpLogClusters = new WORD[ mnCharCapacity ];
+ mpCharWidths = new int[ mnCharCapacity ];
+
+ mnGlyphCount = 0;
+ mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
+ mpGlyphAdvances = new int[ mnGlyphCapacity ];
+ mpOutGlyphs = new WORD[ mnGlyphCapacity ];
+ mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ];
+ mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ];
+
+ long nXOffset = 0;
+ for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
+ mpCharWidths[j] = 0;
+
+ // layout script items
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ for( nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+
+ // initialize glyph specific item info
+ rVisualItem.mnMinGlyphPos = mnGlyphCount;
+ rVisualItem.mnEndGlyphPos = 0;
+ rVisualItem.mnXOffset = nXOffset;
+ //rVisualItem.mnPixelWidth = 0;
+
+ // shortcut ignorable items
+ if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
+ || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
+ {
+ for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
+ mpLogClusters[i] = sal::static_int_cast<WORD>(~0U);
+ continue;
+ }
+
+ // override bidi analysis if requested
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
+ {
+ // FIXME: is this intended ?
+ rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1);
+ rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel;
+ rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
+ }
+
+ // convert the unicodes to glyphs
+ int nGlyphCount = 0;
+ int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
+ HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache,
+ reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
+ nCharCount,
+ mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
+ &rVisualItem.mpScriptItem->a,
+ mpOutGlyphs + rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ &nGlyphCount );
+
+ // find and handle problems in the unicode to glyph conversion
+ if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
+ {
+ // the whole visual item needs a fallback, but make sure that the next
+ // fallback request is limited to the characters in the original request
+ // => this is handled in ImplLayoutArgs::PrepareFallback()
+ rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
+ rVisualItem.IsRTL() );
+
+ // don't bother to do a default layout in a fallback level
+ if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
+ continue;
+
+ // the primitive layout engine is good enough for the default layout
+ rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
+ nRC = (*pScriptShape)( mhDC, &rScriptCache,
+ reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
+ nCharCount,
+ mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
+ &rVisualItem.mpScriptItem->a,
+ mpOutGlyphs + rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ &nGlyphCount );
+
+ if( nRC != 0 )
+ continue;
+
+#if 0 // keep the glyphs for now because they are better than nothing
+ // mark as NotDef glyphs
+ for( i = 0; i < nGlyphCount; ++i )
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0;
+#endif
+ }
+ else if( nRC != 0 )
+ // something undefined happened => give up for this visual item
+ continue;
+ else // if( nRC == 0 )
+ {
+ // check if there are any NotDef glyphs
+ for( i = 0; i < nGlyphCount; ++i )
+ if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
+ break;
+ if( i < nGlyphCount )
+ {
+ // clip charpos limits to the layout string without context
+ int nMinCharPos = rVisualItem.mnMinCharPos;
+ if( nMinCharPos < rArgs.mnMinCharPos )
+ nMinCharPos = rArgs.mnMinCharPos;
+ int nEndCharPos = rVisualItem.mnEndCharPos;
+ if( nEndCharPos > rArgs.mnEndCharPos )
+ nEndCharPos = rArgs.mnEndCharPos;
+ // request fallback for individual NotDef glyphs
+ do
+ {
+ // ignore non-NotDef glyphs
+ if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
+ continue;
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
+ // request fallback for the whole cell that resulted in a NotDef glyph
+ // TODO: optimize algorithm
+ const bool bRTL = rVisualItem.IsRTL();
+ if( !bRTL )
+ {
+ // request fallback for the left-to-right cell
+ for( int c = nMinCharPos; c < nEndCharPos; ++c )
+ {
+ if( mpLogClusters[ c ] == i )
+ {
+ // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
+ if( rArgs.mpStr[ c ] == 0x2060 )
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
+ else
+ // <--
+ rArgs.NeedFallback( c, false );
+ }
+ }
+ }
+ else
+ {
+ // request fallback for the right to left cell
+ for( int c = nEndCharPos; --c >= nMinCharPos; )
+ {
+ if( mpLogClusters[ c ] == i )
+ {
+ // --> HDU/FME 2005-10-25 #i55716# skip WORDJOINER
+ if( rArgs.mpStr[ c ] == 0x2060 )
+ mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
+ else
+ // <--
+ rArgs.NeedFallback( c, true );
+ }
+ }
+ }
+ } while( ++i < nGlyphCount );
+ }
+ }
+
+ // now place the glyphs
+ nRC = (*pScriptPlace)( mhDC, &rScriptCache,
+ mpOutGlyphs + rVisualItem.mnMinGlyphPos,
+ nGlyphCount,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ &rVisualItem.mpScriptItem->a,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
+ &rVisualItem.maABCWidths );
+
+ if( nRC != 0 )
+ continue;
+
+ // calculate the logical char widths from the glyph layout
+ nRC = (*pScriptGetLogicalWidths)(
+ &rVisualItem.mpScriptItem->a,
+ nCharCount, nGlyphCount,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ mpCharWidths + rVisualItem.mnMinCharPos );
+
+ // update the glyph counters
+ mnGlyphCount += nGlyphCount;
+ rVisualItem.mnEndGlyphPos = mnGlyphCount;
+
+ // update nXOffset
+ int nEndGlyphPos;
+ if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
+ for(; i < nEndGlyphPos; ++i )
+ nXOffset += mpGlyphAdvances[ i ];
+
+ // TODO: shrink glyphpos limits to match charpos/fallback limits
+ //pVI->mnMinGlyphPos = nMinGlyphPos;
+ //pVI->mnEndGlyphPos = nEndGlyphPos;
+
+ // drop the superfluous context glyphs
+ TIntVector::const_iterator it = aDropChars.begin();
+ while( it != aDropChars.end() )
+ {
+ // find matching "drop range"
+ int nMinDropPos = *(it++); // begin of drop range
+ if( nMinDropPos >= rVisualItem.mnEndCharPos )
+ break;
+ int nEndDropPos = *(it++); // end of drop range
+ if( nEndDropPos <= rVisualItem.mnMinCharPos )
+ continue;
+ // clip "drop range" to visual item's char range
+ if( nMinDropPos <= rVisualItem.mnMinCharPos )
+ {
+ nMinDropPos = rVisualItem.mnMinCharPos;
+ // drop the whole visual item if possible
+ if( nEndDropPos >= rVisualItem.mnEndCharPos )
+ {
+ rVisualItem.mnEndGlyphPos = 0;
+ break;
+ }
+ }
+ if( nEndDropPos > rVisualItem.mnEndCharPos )
+ nEndDropPos = rVisualItem.mnEndCharPos;
+
+ // drop the glyphs which correspond to the charpos range
+ // drop the corresponding glyphs in the cluster
+ for( int c = nMinDropPos; c < nEndDropPos; ++c )
+ {
+ int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
+ // no need to bother when the cluster was already dropped
+ if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
+ {
+ for(;;)
+ {
+ mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
+ // until the end of visual item
+ if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
+ break;
+ // until the next cluster start
+ if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // scale layout metrics if needed
+ // TODO: does it make the code more simple if the metric scaling
+ // is moved to the methods that need metric scaling (e.g. FillDXArray())?
+ if( mfFontScale != 1.0 )
+ {
+ mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
+
+ for( i = 0; i < mnItemCount; ++i )
+ mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
+
+ mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
+ mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
+ mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
+ // mpJustifications are still NULL
+ }
+
+ for( i = mnSubStringMin; i < nSubStringEnd; ++i )
+ mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+// calculate the range of relevant glyphs for this visual item
+bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
+ int& rMinGlyphPos, int& rEndGlyphPos ) const
+{
+ // return early when nothing of interest in this item
+ if( rVisualItem.IsEmpty()
+ || (rVisualItem.mnEndCharPos <= mnMinCharPos)
+ || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
+ return false;
+
+ // default: subrange is complete range
+ rMinGlyphPos = rVisualItem.mnMinGlyphPos;
+ rEndGlyphPos = rVisualItem.mnEndGlyphPos;
+
+ // return early when the whole item is of interest
+ if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
+ && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
+ return true;
+
+ // get glyph range from char range by looking at cluster boundries
+ // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
+ rMinGlyphPos = rVisualItem.mnEndGlyphPos;
+ int nMaxGlyphPos = 0;
+
+ int i = mnMinCharPos;
+ if( i < rVisualItem.mnMinCharPos )
+ i = rVisualItem.mnMinCharPos;
+ int nCharPosLimit = rVisualItem.mnEndCharPos;
+ if( nCharPosLimit > mnEndCharPos )
+ nCharPosLimit = mnEndCharPos;
+ for(; i < nCharPosLimit; ++i )
+ {
+ int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
+ if( rMinGlyphPos > n )
+ rMinGlyphPos = n;
+ if( nMaxGlyphPos < n )
+ nMaxGlyphPos = n;
+ }
+ if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
+ nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
+
+ // extend the glyph range to account for all glyphs in referenced clusters
+ if( !rVisualItem.IsRTL() ) // LTR-item
+ {
+ // extend to rightmost glyph of rightmost referenced cluster
+ for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
+ if( mpVisualAttrs[i].fClusterStart )
+ break;
+ }
+ else // RTL-item
+ {
+ // extend to leftmost glyph of leftmost referenced cluster
+ for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
+ if( mpVisualAttrs[i].fClusterStart )
+ break;
+ }
+ rEndGlyphPos = nMaxGlyphPos + 1;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
+ int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const
+{
+ // HACK to allow fake-glyph insertion (e.g. for kashidas)
+ // TODO: use iterator idiom instead of GetNextGlyphs(...)
+ // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
+ int nSubIter = nStartx8 & 0xff;
+ int nStart = nStartx8 >> 8;
+
+ // check the glyph iterator
+ if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs
+ return 0;
+
+ // find the visual item for the nStart glyph position
+ int nItem = 0;
+ const VisualItem* pVI = mpVisualItems;
+ if( nStart <= 0 ) // nStart<=0 requests the first visible glyph
+ {
+ // find first visible item
+ for(; nItem < mnItemCount; ++nItem, ++pVI )
+ if( !pVI->IsEmpty() )
+ break;
+ // it is possible that there are glyphs but no valid visual item
+ // TODO: get rid of these visual items more early
+ if( nItem < mnItemCount )
+ nStart = pVI->mnMinGlyphPos;
+ }
+ else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1
+ {
+ --nStart;
+
+ // find matching item
+ for(; nItem < mnItemCount; ++nItem, ++pVI )
+ if( (nStart >= pVI->mnMinGlyphPos)
+ && (nStart < pVI->mnEndGlyphPos) )
+ break;
+ }
+
+ // after the last visual item there are no more glyphs
+ if( (nItem >= mnItemCount) || (nStart < 0) )
+ {
+ nStartx8 = (mnGlyphCount + 1) << 8;
+ return 0;
+ }
+
+ // calculate the first glyph in the next visual item
+ int nNextItemStart = mnGlyphCount;
+ while( ++nItem < mnItemCount )
+ {
+ if( mpVisualItems[nItem].IsEmpty() )
+ continue;
+ nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
+ break;
+ }
+
+ // get the range of relevant glyphs in this visual item
+ int nMinGlyphPos, nEndGlyphPos;
+ bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
+ DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" );
+ if( !bRC )
+ {
+ nStartx8 = (mnGlyphCount + 1) << 8;
+ return 0;
+ }
+
+ // make sure nStart is inside the range of relevant glyphs
+ if( nStart < nMinGlyphPos )
+ nStart = nMinGlyphPos;
+
+ // calculate the start glyph xoffset relative to layout's base position,
+ // advance to next visual glyph position by using adjusted glyph widths
+ // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
+ long nXOffset = pVI->mnXOffset;
+ const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
+ for( int i = nMinGlyphPos; i < nStart; ++i )
+ nXOffset += pGlyphWidths[ i ];
+
+ // adjust the nXOffset relative to glyph cluster start
+ int c = mnMinCharPos;
+ if( !pVI->IsRTL() ) // LTR-case
+ {
+ // LTR case: subtract the remainder of the cell from xoffset
+ int nTmpIndex = mpLogClusters[c];
+ while( (--c >= pVI->mnMinCharPos)
+ && (nTmpIndex == mpLogClusters[c]) )
+ nXOffset -= mpCharWidths[c];
+ }
+ else // RTL-case
+ {
+ // RTL case: add the remainder of the cell from xoffset
+ int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
+ while( (--c >= pVI->mnMinCharPos)
+ && (nTmpIndex == mpLogClusters[c]) )
+ nXOffset += mpCharWidths[c];
+
+ // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
+ if( mpJustifications && !bManualCellAlign )
+ nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
+ }
+
+ // create mpGlyphs2Chars[] if it is needed later
+ if( pCharPosAry && !mpGlyphs2Chars )
+ {
+ // create and reset the new array
+ mpGlyphs2Chars = new int[ mnGlyphCapacity ];
+ for( int i = 0; i < mnGlyphCount; ++i )
+ mpGlyphs2Chars[i] = -1;
+ // calculate the char->glyph mapping
+ for( nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ // ignore invisible visual items
+ const VisualItem& rVI = mpVisualItems[ nItem ];
+ if( rVI.IsEmpty() )
+ continue;
+ // calculate the mapping by using mpLogClusters[]
+ // mpGlyphs2Chars[] should obey the logical order
+ // => reversing the loop does this by overwriting higher logicals
+ for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
+ {
+ int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
+ mpGlyphs2Chars[i] = c;
+ }
+ }
+ }
+
+ // calculate the absolute position of the first result glyph in pixel units
+ const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
+ Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
+ rPos = GetDrawPosition( aRelativePos );
+
+ // fill the result arrays
+ int nCount = 0;
+ while( nCount < nLen )
+ {
+ // prepare return values
+ sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
+ int nGlyphWidth = pGlyphWidths[ nStart ];
+ int nCharPos = -1; // no need to determine charpos
+ if( mpGlyphs2Chars ) // unless explicitly requested+provided
+ nCharPos = mpGlyphs2Chars[ nStart ];
+
+ // inject kashida glyphs if needed
+ if( !mbDisableGlyphInjection
+ && mpJustifications
+ && mnMinKashidaWidth
+ && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
+ {
+ // prepare draw position adjustment
+ int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
+ // calculate space available for the injected glyphs
+ nGlyphWidth = mpGlyphAdvances[ nStart ];
+ const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
+ const int nToFillWidth = nExtraWidth - nExtraOfs;
+ if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room
+ || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
+ {
+ // handle if there is not sufficient room for a full glyph
+ if( nToFillWidth < mnMinKashidaWidth )
+ {
+ // overlap it with the previously injected glyph if possible
+ int nOverlap = mnMinKashidaWidth - nToFillWidth;
+ // else overlap it with both neighboring glyphs
+ if( nSubIter <= 1 )
+ nOverlap /= 2;
+ nExtraOfs -= nOverlap;
+ }
+ nGlyphWidth = mnMinKashidaWidth;
+ aGlyphId = mnMinKashidaGlyph;
+ nCharPos = -1;
+ }
+ else
+ {
+ nExtraOfs += nToFillWidth; // at right of cell
+ nSubIter = 0; // done with glyph injection
+ }
+ if( !bManualCellAlign )
+ nExtraOfs -= nExtraWidth; // adjust for right-aligned cells
+
+ // adjust the draw position for the injected-glyphs case
+ if( nExtraOfs )
+ {
+ aRelativePos.X() += nExtraOfs;
+ rPos = GetDrawPosition( aRelativePos );
+ }
+ }
+
+ // update return values
+ *(pGlyphs++) = aGlyphId;
+ if( pGlyphAdvances )
+ *(pGlyphAdvances++) = nGlyphWidth;
+ if( pCharPosAry )
+ *(pCharPosAry++) = nCharPos;
+
+ // increment counter of returned glyphs
+ ++nCount;
+
+ // reduce code complexity by returning early in glyph-injection case
+ if( nSubIter != 0 )
+ break;
+
+ // stop after the last visible glyph in this visual item
+ if( ++nStart >= nEndGlyphPos )
+ {
+ nStart = nNextItemStart;
+ break;
+ }
+
+ // RTL-justified glyph positioning is not easy
+ // simplify the code by just returning only one glyph at a time
+ if( mpJustifications && pVI->IsRTL() )
+ break;
+
+ // stop when the x-position of the next glyph is unexpected
+ if( !pGlyphAdvances )
+ if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
+ || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
+ break;
+
+ // stop when the y-position of the next glyph is unexpected
+ if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
+ break;
+ }
+
+ ++nStart;
+ nStartx8 = (nStart << 8) + nSubIter;
+ return nCount;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
+{
+ DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" );
+ int nStart = nStartx8 >> 8;
+ if( nStart > mnGlyphCount )
+ return;
+
+ VisualItem* pVI = mpVisualItems;
+ int nMinGlyphPos = 0, nEndGlyphPos;
+ if( nStart == 0 ) // nStart==0 for first visible glyph
+ {
+ for( int i = mnItemCount; --i >= 0; ++pVI )
+ if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
+ break;
+ nStart = nMinGlyphPos;
+ DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" );
+ }
+ else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
+ {
+ --nStart;
+ for( int i = mnItemCount; --i >= 0; ++pVI )
+ if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
+ break;
+ bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
+ (void)bRC; // avoid var-not-used warning
+ DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" );
+ }
+
+ long nDelta = nNewXPos - pVI->mnXOffset;
+ if( nStart > nMinGlyphPos )
+ {
+ // move the glyph by expanding its left glyph but ignore dropped glyphs
+ int i, nLastUndropped = nMinGlyphPos - 1;
+ for( i = nMinGlyphPos; i < nStart; ++i )
+ {
+ if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
+ {
+ nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
+ nLastUndropped = i;
+ }
+ }
+ if (nLastUndropped >= nMinGlyphPos)
+ {
+ mpGlyphAdvances[ nLastUndropped ] += nDelta;
+ if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta;
+ }
+ else
+ {
+ pVI->mnXOffset += nDelta;
+ }
+ }
+ else
+ {
+ // move the visual item by having an offset
+ pVI->mnXOffset += nDelta;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::DropGlyph( int nStartx8 )
+{
+ DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" );
+ int nStart = nStartx8 >> 8;
+ DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" );
+
+ if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1
+ --nStart;
+ else // nStart<=0 for first visible glyph
+ {
+ VisualItem* pVI = mpVisualItems;
+ for( int i = mnItemCount, nDummy; --i >= 0; ++pVI )
+ if( GetItemSubrange( *pVI, nStart, nDummy ) )
+ break;
+ DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" );
+ int nOffset = 0;
+ int j = pVI->mnMinGlyphPos;
+ while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++;
+ if (j == nStart)
+ {
+ pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]);
+ }
+ }
+
+ mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::Simplify( bool /*bIsBase*/ )
+{
+ static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH;
+ int i;
+ // if there are no dropped glyphs don't bother
+ for( i = 0; i < mnGlyphCount; ++i )
+ if( mpOutGlyphs[ i ] == cDroppedGlyph )
+ break;
+ if( i >= mnGlyphCount )
+ return;
+
+ // prepare for sparse layout
+ // => make sure mpGlyphs2Chars[] exists
+ if( !mpGlyphs2Chars )
+ {
+ mpGlyphs2Chars = new int[ mnGlyphCapacity ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphs2Chars[ i ] = -1;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ // skip invisible items
+ VisualItem& rVI = mpVisualItems[ nItem ];
+ if( rVI.IsEmpty() )
+ continue;
+ for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; )
+ {
+ int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
+ mpGlyphs2Chars[ j ] = i;
+ }
+ }
+ }
+
+ // remove the dropped glyphs
+ const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVI = mpVisualItems[ nItem ];
+ if( rVI.IsEmpty() )
+ continue;
+
+ // mark replaced character widths
+ for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i )
+ {
+ int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
+ if( mpOutGlyphs[ j ] == cDroppedGlyph )
+ mpCharWidths[ i ] = 0;
+ }
+
+ // handle dropped glyphs at start of visual item
+ int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos;
+ GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos );
+ i = nMinGlyphPos;
+ while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) )
+ {
+ //rVI.mnXOffset += pGlyphWidths[ i ];
+ rVI.mnMinGlyphPos = ++i;
+ }
+
+ // when all glyphs in item got dropped mark it as empty
+ if( i >= nEndGlyphPos )
+ {
+ rVI.mnEndGlyphPos = 0;
+ continue;
+ }
+ // If there are still glyphs in the cluster and mnMinGlyphPos
+ // has changed then we need to remove the dropped glyphs at start
+ // to correct logClusters, which is unsigned and relative to the
+ // item start.
+ if (rVI.mnMinGlyphPos != nOrigMinGlyphPos)
+ {
+ // drop any glyphs in the visual item outside the range
+ for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++)
+ mpOutGlyphs[ i ] = cDroppedGlyph;
+ rVI.mnMinGlyphPos = i = nOrigMinGlyphPos;
+ }
+
+ // handle dropped glyphs in the middle of visual item
+ for(; i < nEndGlyphPos; ++i )
+ if( mpOutGlyphs[ i ] == cDroppedGlyph )
+ break;
+ int j = i;
+ while( ++i < nEndGlyphPos )
+ {
+ if( mpOutGlyphs[ i ] == cDroppedGlyph )
+ continue;
+ mpOutGlyphs[ j ] = mpOutGlyphs[ i ];
+ mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ];
+ mpVisualAttrs[ j ] = mpVisualAttrs[ i ];
+ mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ];
+ if( mpJustifications )
+ mpJustifications[ j ] = mpJustifications[ i ];
+ const int k = mpGlyphs2Chars[ i ];
+ mpGlyphs2Chars[ j ] = k;
+ const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos;
+ mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos);
+ }
+
+ rVI.mnEndGlyphPos = j;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::DrawText( SalGraphics& ) const
+{
+ HFONT hOrigFont = DisableFontScaling();
+
+ int nBaseClusterOffset = 0;
+ int nBaseGlyphPos = -1;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+
+ // skip if there is nothing to display
+ int nMinGlyphPos, nEndGlyphPos;
+ if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
+ continue;
+
+ if( nBaseGlyphPos < 0 )
+ {
+ // adjust draw position relative to cluster start
+ if( rVisualItem.IsRTL() )
+ nBaseGlyphPos = nEndGlyphPos - 1;
+ else
+ nBaseGlyphPos = nMinGlyphPos;
+
+ const int* pGlyphWidths;
+ if( mpJustifications )
+ pGlyphWidths = mpJustifications;
+ else
+ pGlyphWidths = mpGlyphAdvances;
+
+ int i = mnMinCharPos;
+ while( (--i >= rVisualItem.mnMinCharPos)
+ && (nBaseGlyphPos == mpLogClusters[i]) )
+ nBaseClusterOffset += mpCharWidths[i];
+
+ if( !rVisualItem.IsRTL() )
+ nBaseClusterOffset = -nBaseClusterOffset;
+ }
+
+ // now draw the matching glyphs in this item
+ Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
+ Point aPos = GetDrawPosition( aRelPos );
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ (*pScriptTextOut)( mhDC, &rScriptCache,
+ aPos.X(), aPos.Y(), 0, NULL,
+ &rVisualItem.mpScriptItem->a, NULL, 0,
+ mpOutGlyphs + nMinGlyphPos,
+ nEndGlyphPos - nMinGlyphPos,
+ mpGlyphAdvances + nMinGlyphPos,
+ mpJustifications ? mpJustifications + nMinGlyphPos : NULL,
+ mpGlyphOffsets + nMinGlyphPos );
+ }
+
+ if( hOrigFont )
+ DeleteFont( SelectFont( mhDC, hOrigFont ) );
+}
+
+// -----------------------------------------------------------------------
+
+long UniscribeLayout::FillDXArray( long* pDXArray ) const
+{
+ // calculate width of the complete layout
+ long nWidth = mnBaseAdv;
+ for( int nItem = mnItemCount; --nItem >= 0; )
+ {
+ const VisualItem& rVI = mpVisualItems[ nItem ];
+
+ // skip if there is nothing to display
+ int nMinGlyphPos, nEndGlyphPos;
+ if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) )
+ continue;
+
+ // width = xoffset + width of last item
+ nWidth = rVI.mnXOffset;
+ const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
+ for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ nWidth += pGlyphWidths[i];
+ break;
+ }
+
+ // copy the virtual char widths into pDXArray[]
+ if( pDXArray )
+ for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
+ pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ];
+
+ return nWidth;
+}
+
+// -----------------------------------------------------------------------
+
+int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+{
+ long nWidth = 0;
+ for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
+ {
+ nWidth += mpCharWidths[ i ] * nFactor;
+
+ // check if the nMaxWidth still fits the current sub-layout
+ if( nWidth >= nMaxWidth )
+ {
+ // go back to cluster start
+ // we have to find the visual item first since the mpLogClusters[]
+ // needed to find the cluster start is relative to to the visual item
+ int nMinGlyphIndex = 0;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
+ if( (i >= rVisualItem.mnMinCharPos)
+ && (i < rVisualItem.mnEndCharPos) )
+ break;
+ }
+ // now go back to the matching cluster start
+ do
+ {
+ int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex;
+ if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart )
+ return i;
+ } while( --i >= mnMinCharPos );
+
+ // if the cluster starts before the start of the visual item
+ // then set the visual breakpoint before this item
+ return mnMinCharPos;
+ }
+
+ // the visual break also depends on the nCharExtra between the characters
+ nWidth += nCharExtra;
+ }
+
+ // the whole layout did fit inside the nMaxWidth
+ return STRING_LEN;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
+{
+ int i;
+ for( i = 0; i < nMaxIdx; ++i )
+ pCaretXArray[ i ] = -1;
+ long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) );
+ for( i = 0; i <= mnGlyphCount; ++i )
+ pGlyphPos[ i ] = -1;
+
+ long nXPos = 0;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ if( rVisualItem.IsEmpty() )
+ continue;
+
+ // get glyph positions
+ // TODO: handle when rVisualItem's glyph range is only partially used
+ for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
+ {
+ pGlyphPos[ i ] = nXPos;
+ nXPos += mpGlyphAdvances[ i ];
+ }
+ // rightmost position of this visualitem
+ pGlyphPos[ i ] = nXPos;
+
+ // convert glyph positions to character positions
+ i = rVisualItem.mnMinCharPos;
+ if( i < mnMinCharPos )
+ i = mnMinCharPos;
+ for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i )
+ {
+ int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
+ int nCurrIdx = i * 2;
+ if( !rVisualItem.IsRTL() )
+ {
+ // normal positions for LTR case
+ pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ];
+ pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ];
+ }
+ else
+ {
+ // reverse positions for RTL case
+ pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ];
+ pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ];
+ }
+ }
+ }
+
+ // fixup unknown character positions to neighbor
+ for( i = 0; i < nMaxIdx; ++i )
+ {
+ if( pCaretXArray[ i ] >= 0 )
+ nXPos = pCaretXArray[ i ];
+ else
+ pCaretXArray[ i ] = nXPos;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+
+ // adjust positions if requested
+ if( rArgs.mpDXArray )
+ ApplyDXArray( rArgs );
+ else if( rArgs.mnLayoutWidth )
+ Justify( rArgs.mnLayoutWidth );
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
+{
+ const long* pDXArray = rArgs.mpDXArray;
+
+ // increase char widths in string range to desired values
+ bool bModified = false;
+ int nOldWidth = 0;
+ DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
+ int i,j;
+ for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j )
+ {
+ int nNewCharWidth = (pDXArray[j] - nOldWidth);
+ // TODO: nNewCharWidth *= mnUnitsPerPixel;
+ if( mpCharWidths[i] != nNewCharWidth )
+ {
+ mpCharWidths[i] = nNewCharWidth;
+ bModified = true;
+ }
+ nOldWidth = pDXArray[j];
+ }
+
+ if( !bModified )
+ return;
+
+ // initialize justifications array
+ mpJustifications = new int[ mnGlyphCapacity ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpJustifications[ i ] = mpGlyphAdvances[ i ];
+
+ // apply new widths to script items
+ long nXOffset = 0;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+
+ // set the position of this visual item
+ rVisualItem.mnXOffset = nXOffset;
+
+ // ignore empty visual items
+ if( rVisualItem.IsEmpty() )
+ {
+ for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++)
+ nXOffset += mpCharWidths[i];
+ continue;
+ }
+ // ignore irrelevant visual items
+ if( (rVisualItem.mnMinCharPos >= mnEndCharPos)
+ || (rVisualItem.mnEndCharPos <= mnMinCharPos) )
+ continue;
+
+ // if needed prepare special handling for arabic justification
+ rVisualItem.mbHasKashidas = false;
+ if( rVisualItem.IsRTL() )
+ {
+ for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
+ if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification
+ { // excluding SCRIPT_JUSTIFY_NONE
+ // yes
+ rVisualItem.mbHasKashidas = true;
+ // so prepare for kashida handling
+ InitKashidaHandling();
+ break;
+ }
+
+ if( rVisualItem.HasKashidas() )
+ for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
+ {
+ // TODO: check if we still need this hack after correction of kashida placing?
+ // (i87688): apparently yes, we still need it!
+ if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE )
+ // usp decided that justification can't be applied here
+ // but maybe our Kashida algorithm thinks differently.
+ // To avoid trouble (gaps within words, last character of
+ // a word gets a Kashida appended) override this.
+
+ // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE
+ // just because this previous hack (which I haven't understand, sorry) used
+ // the same value to replace. Don't know if this is really the best
+ // thing to do, but it seems to fix things
+ mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
+ }
+ }
+
+ // convert virtual charwidths to glyph justification values
+ HRESULT nRC = (*pScriptApplyLogicalWidth)(
+ mpCharWidths + rVisualItem.mnMinCharPos,
+ rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos,
+ rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
+ mpLogClusters + rVisualItem.mnMinCharPos,
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ &rVisualItem.mpScriptItem->a,
+ &rVisualItem.maABCWidths,
+ mpJustifications + rVisualItem.mnMinGlyphPos );
+
+ if( nRC != 0 )
+ {
+ delete[] mpJustifications;
+ mpJustifications = NULL;
+ break;
+ }
+
+ // to prepare for the next visual item
+ // update nXOffset to the next items position
+ // before the mpJustifications[] array gets modified
+ int nMinGlyphPos, nEndGlyphPos;
+ if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
+ {
+ for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ nXOffset += mpJustifications[ i ];
+
+ if( rVisualItem.mbHasKashidas )
+ KashidaItemFix( nMinGlyphPos, nEndGlyphPos );
+ }
+
+ // workaround needed for older USP versions:
+ // right align the justification-adjusted glyphs in their cells for RTL-items
+ // unless the right alignment is done by inserting kashidas
+ if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
+ {
+ for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ {
+ const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i];
+ // #i99862# skip diacritics, we mustn't add extra justification to diacritics
+ int nIdxAdd = i - 1;
+ while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] )
+ --nIdxAdd;
+ if( nIdxAdd < nMinGlyphPos )
+ rVisualItem.mnXOffset += nXOffsetAdjust;
+ else
+ mpJustifications[nIdxAdd] += nXOffsetAdjust;
+ mpJustifications[i] -= nXOffsetAdjust;
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::InitKashidaHandling()
+{
+ if( mnMinKashidaGlyph != 0 ) // already initialized
+ return;
+
+ mrWinFontEntry.InitKashidaHandling( mhDC );
+ mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth());
+ mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph();
+}
+
+// adjust the kashida placement matching to the WriterEngine
+void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
+{
+ // workaround needed for all known USP versions:
+ // ApplyLogicalWidth does not match ScriptJustify behaviour
+ for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
+ {
+ // check for vowels
+ if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ])
+ && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types
+ { // including SCRIPT_JUSTIFY_NONE
+ // vowel, we do it like ScriptJustify does
+ // the vowel gets the extra width
+ long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
+ mpJustifications [ i ] = mpGlyphAdvances [ i ];
+ mpJustifications [ i - 1 ] += nSpaceAdded;
+ }
+ }
+
+ // redistribute the widths for kashidas
+ for( int i = nMinGlyphPos; i < nEndGlyphPos; )
+ KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
+}
+
+bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
+{
+ // doing pixel work within a word.
+ // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
+
+ // find the next kashida
+ int nMinPos = *pnCurrentPos;
+ int nMaxPos = *pnCurrentPos;
+ for( int i = nMaxPos; i < nEndGlyphPos; ++i )
+ {
+ if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK)
+ && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) )
+ break;
+ nMaxPos = i;
+ }
+ *pnCurrentPos = nMaxPos + 1;
+ if( nMinPos == nMaxPos )
+ return false;
+
+ // calculate the available space for an extra kashida
+ long nMaxAdded = 0;
+ int nKashPos = -1;
+ for( int i = nMaxPos; i >= nMinPos; --i )
+ {
+ long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
+ if( nSpaceAdded > nMaxAdded )
+ {
+ nKashPos = i;
+ nMaxAdded = nSpaceAdded;
+ }
+ }
+
+ // return early if there is no need for an extra kashida
+ if ( nMaxAdded <= 0 )
+ return false;
+ // return early if there is not enough space for an extra kashida
+ if( 2*nMaxAdded < mnMinKashidaWidth )
+ return false;
+
+ // redistribute the extra spacing to the kashida position
+ for( int i = nMinPos; i <= nMaxPos; ++i )
+ {
+ if( i == nKashPos )
+ continue;
+ // everything else should not have extra spacing
+ long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
+ if( nSpaceAdded > 0 )
+ {
+ mpJustifications[ i ] -= nSpaceAdded;
+ mpJustifications[ nKashPos ] += nSpaceAdded;
+ }
+ }
+
+ // check if we fulfill minimal kashida width
+ long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
+ if( nSpaceAdded < mnMinKashidaWidth )
+ {
+ // ugly: steal some pixels
+ long nSteal = 1;
+ if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos)))
+ nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos);
+ for( int i = nMinPos; i <= nMaxPos; ++i )
+ {
+ if( i == nKashPos )
+ continue;
+ nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal );
+ if ( nSteal > 0 )
+ {
+ mpJustifications [ i ] -= nSteal;
+ mpJustifications [ nKashPos ] += nSteal;
+ nSpaceAdded += nSteal;
+ }
+ if( nSpaceAdded >= mnMinKashidaWidth )
+ return true;
+ }
+ }
+
+ // blank padding
+ long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
+ if( nSpaceMissing > 0 )
+ {
+ // inner glyph: distribute extra space evenly
+ if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) )
+ {
+ mpJustifications [ nKashPos ] += nSpaceMissing;
+ long nHalfSpace = nSpaceMissing / 2;
+ mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
+ mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
+ }
+ // rightmost: left glyph gets extra space
+ else if( nMinPos > nMinGlyphPos )
+ {
+ mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
+ mpJustifications [ nKashPos ] += nSpaceMissing;
+ }
+ // leftmost: right glyph gets extra space
+ else if( nMaxPos < nEndGlyphPos - 1 )
+ {
+ mpJustifications [ nKashPos ] += nSpaceMissing;
+ mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
+ }
+ else
+ return false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+void UniscribeLayout::Justify( long nNewWidth )
+{
+ long nOldWidth = 0;
+ int i;
+ for( i = mnMinCharPos; i < mnEndCharPos; ++i )
+ nOldWidth += mpCharWidths[ i ];
+ if( nOldWidth <= 0 )
+ return;
+
+ nNewWidth *= mnUnitsPerPixel; // convert into font units
+ if( nNewWidth == nOldWidth )
+ return;
+ // prepare to distribute the extra width evenly among the visual items
+ const double fStretch = (double)nNewWidth / nOldWidth;
+
+ // initialize justifications array
+ mpJustifications = new int[ mnGlyphCapacity ];
+ for( i = 0; i < mnGlyphCapacity; ++i )
+ mpJustifications[ i ] = mpGlyphAdvances[ i ];
+
+ // justify stretched script items
+ long nXOffset = 0;
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ if( rVisualItem.IsEmpty() )
+ continue;
+
+ if( (rVisualItem.mnMinCharPos < mnEndCharPos)
+ && (rVisualItem.mnEndCharPos > mnMinCharPos) )
+ {
+ long nItemWidth = 0;
+ for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
+ nItemWidth += mpCharWidths[ i ];
+ nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5);
+
+ HRESULT nRC = (*pScriptJustify) (
+ mpVisualAttrs + rVisualItem.mnMinGlyphPos,
+ mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
+ rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
+ nItemWidth,
+ mnMinKashidaWidth,
+ mpJustifications + rVisualItem.mnMinGlyphPos );
+
+ rVisualItem.mnXOffset = nXOffset;
+ nXOffset += nItemWidth;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
+{
+ // we have to find the visual item first since the mpLogClusters[]
+ // needed to find the cluster start is relative to to the visual item
+ int nMinGlyphIndex = -1;
+ for( int nItem = 0; nItem < mnItemCount; ++nItem )
+ {
+ const VisualItem& rVisualItem = mpVisualItems[ nItem ];
+ if( (nCharPos >= rVisualItem.mnMinCharPos)
+ && (nCharPos < rVisualItem.mnEndCharPos) )
+ {
+ nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
+ break;
+ }
+ }
+ // Invalid char pos or leftmost glyph in visual item
+ if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] )
+ return false;
+
+// This test didn't give the expected results
+/* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
+ // two chars, one glyph
+ return false;*/
+
+ const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
+ if( nGlyphPos <= 0 )
+ return true;
+ // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
+ // and not SCRIPT_JUSTIFY_ARABIC_BLANK
+ // special case: glyph to the left is vowel (no advance width)
+ if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK
+ || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE
+ && mpGlyphAdvances [ nGlyphPos-1 ] ))
+ return false;
+ return true;
+}
+
+#endif // USE_UNISCRIBE
+
+#ifdef ENABLE_GRAPHITE
+
+class GraphiteLayoutWinImpl : public GraphiteLayout
+{
+public:
+ GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont)
+ throw()
+ : GraphiteLayout(font), mrFont(rFont) {};
+ virtual ~GraphiteLayoutWinImpl() throw() {};
+ virtual sal_GlyphId getKashidaGlyph(int & rWidth);
+private:
+ ImplWinFontEntry & mrFont;
+};
+
+sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth)
+{
+ rWidth = mrFont.GetMinKashidaWidth();
+ return mrFont.GetMinKashidaGlyph();
+}
+
+// This class uses the SIL Graphite engine to provide complex text layout services to the VCL
+// @author tse
+//
+class GraphiteWinLayout : public WinLayout
+{
+private:
+ mutable gr::WinFont mpFont;
+ grutils::GrFeatureParser * mpFeatures;
+ mutable GraphiteLayoutWinImpl maImpl;
+public:
+ GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE);
+
+ static bool IsGraphiteEnabledFont(HDC hDC) throw();
+
+ // used by upper layers
+ virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout
+ virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc.
+ // virtual void InitFont() const;
+ virtual void DrawText( SalGraphics& ) const;
+
+ // methods using string indexing
+ virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const;
+ virtual long FillDXArray( long* pDXArray ) const;
+
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
+
+ // methods using glyph indexing
+ virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&,
+ long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const;
+
+ // used by glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos );
+ virtual void DropGlyph( int nStart );
+ virtual void Simplify( bool bIsBase );
+ ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; };
+protected:
+ virtual void ReplaceDC(gr::Segment & segment) const;
+ virtual void RestoreDC(gr::Segment & segment) const;
+};
+
+bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw()
+{
+ return gr::WinFont::FontHasGraphiteTables(hDC);
+}
+
+GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw()
+ : WinLayout(hDC, rWFD, rWFE), mpFont(hDC),
+ maImpl(mpFont, rWFE)
+{
+ const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage );
+ rtl::OString name = rtl::OUStringToOString(
+ rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
+ if (nFeat > 0)
+ {
+ rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
+ mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr());
+ }
+ else
+ {
+ mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr());
+ }
+ maImpl.SetFeatures(mpFeatures);
+}
+
+void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const
+{
+ COLORREF color = GetTextColor(mhDC);
+ dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC);
+ SetTextColor(mhDC, color);
+}
+
+void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const
+{
+ dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC();
+}
+
+bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
+{
+ HFONT hUnRotatedFont;
+ if (args.mnOrientation)
+ {
+ // Graphite gets very confused if the font is rotated
+ LOGFONTW aLogFont;
+ ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
+ aLogFont.lfEscapement = 0;
+ aLogFont.lfOrientation = 0;
+ hUnRotatedFont = ::CreateFontIndirectW( &aLogFont);
+ ::SelectFont(mhDC, hUnRotatedFont);
+ }
+ WinLayout::AdjustLayout(args);
+ mpFont.replaceDC(mhDC);
+ maImpl.SetFontScale(WinLayout::mfFontScale);
+ //bool succeeded = maImpl.LayoutText(args);
+#ifdef GRCACHE
+ GrSegRecord * pSegRecord = NULL;
+ gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord);
+#else
+ gr::Segment * pSegment = maImpl.CreateSegment(args);
+#endif
+ bool bSucceeded = false;
+ if (pSegment)
+ {
+ // replace the DC on the font within the segment
+ ReplaceDC(*pSegment);
+ // create glyph vectors
+#ifdef GRCACHE
+ bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord);
+#else
+ bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
+#endif
+ // restore original DC
+ RestoreDC(*pSegment);
+#ifdef GRCACHE
+ if (pSegRecord) pSegRecord->unlock();
+ else delete pSegment;
+#else
+ delete pSegment;
+#endif
+ }
+ mpFont.restoreDC();
+ if (args.mnOrientation)
+ {
+ // restore the rotated font
+ ::SelectFont(mhDC, mhFont);
+ ::DeleteObject(hUnRotatedFont);
+ }
+ return bSucceeded;
+}
+
+void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs)
+{
+ WinLayout::AdjustLayout(rArgs);
+ maImpl.DrawBase() = WinLayout::maDrawBase;
+ maImpl.DrawOffset() = WinLayout::maDrawOffset;
+ if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray)
+ {
+ mrWinFontEntry.InitKashidaHandling(mhDC);
+ }
+ maImpl.AdjustLayout(rArgs);
+}
+
+void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const
+{
+ HFONT hOrigFont = DisableFontScaling();
+ HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).mhDC;
+ maImpl.DrawBase() = WinLayout::maDrawBase;
+ maImpl.DrawOffset() = WinLayout::maDrawOffset;
+ const int MAX_GLYPHS = 2;
+ sal_GlyphId glyphIntStr[MAX_GLYPHS];
+ WORD glyphWStr[MAX_GLYPHS];
+ int glyphIndex = 0;
+ Point aPos(0,0);
+ int nGlyphs = 0;
+ do
+ {
+ nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex);
+ if (nGlyphs < 1)
+ break;
+ std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr);
+ ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX,
+ NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL);
+ } while (nGlyphs);
+ if( hOrigFont )
+ DeleteFont( SelectFont( mhDC, hOrigFont ) );
+}
+
+int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
+{
+ mpFont.replaceDC(mhDC);
+ int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor);
+ mpFont.restoreDC();
+ return nBreak;
+}
+
+long GraphiteWinLayout::FillDXArray( long* pDXArray ) const
+{
+ return maImpl.FillDXArray(pDXArray);
+}
+
+void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const
+{
+ maImpl.GetCaretPositions(nArraySize, pCaretXArray);
+}
+
+int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out,
+ ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const
+{
+ maImpl.DrawBase() = WinLayout::maDrawBase;
+ maImpl.DrawOffset() = WinLayout::maDrawOffset;
+ return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index);
+}
+
+void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos )
+{
+ maImpl.MoveGlyph(glyph_idx, new_x_pos);
+}
+
+void GraphiteWinLayout::DropGlyph( int glyph_idx )
+{
+ maImpl.DropGlyph(glyph_idx);
+}
+
+void GraphiteWinLayout::Simplify( bool is_base )
+{
+ maImpl.Simplify(is_base);
+}
+#endif // ENABLE_GRAPHITE
+// =======================================================================
+
+SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
+{
+ DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL");
+
+ WinLayout* pWinLayout = NULL;
+
+ const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ];
+ ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ];
+
+#if defined( USE_UNISCRIBE )
+ if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED)
+ && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine
+ {
+#ifdef ENABLE_GRAPHITE
+ if (rFontFace.SupportsGraphite())
+ pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
+ else
+#endif // ENABLE_GRAPHITE
+ // script complexity is determined in upper layers
+ pWinLayout = new UniscribeLayout( mhDC, rFontFace, rFontInstance );
+ // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
+ // the created UniscribeLayout, otherwise the data passed into the
+ // constructor might become invalid too early
+ }
+ else
+#endif // USE_UNISCRIBE
+ {
+#ifdef GCP_KERN_HACK
+ if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() )
+ {
+ // TODO: directly cache kerning info in the rFontInstance
+ // TODO: get rid of kerning methods+data in WinSalGraphics object
+ GetKernPairs( 0, NULL );
+ rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
+ }
+#endif // GCP_KERN_HACK
+
+ BYTE eCharSet = ANSI_CHARSET;
+ if( mpLogFont )
+ eCharSet = mpLogFont->lfCharSet;
+#ifdef ENABLE_GRAPHITE
+ if (rFontFace.SupportsGraphite())
+ pWinLayout = new GraphiteWinLayout(mhDC, rFontFace, rFontInstance);
+ else
+#endif // ENABLE_GRAPHITE
+ pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance );
+ }
+
+ if( mfFontScale != 1.0 )
+ pWinLayout->SetFontScale( mfFontScale );
+
+ return pWinLayout;
+}
+
+// -----------------------------------------------------------------------
+
+int WinSalGraphics::GetMinKashidaWidth()
+{
+ if( !mpWinFontEntry[0] )
+ return 0;
+ mpWinFontEntry[0]->InitKashidaHandling( mhDC );
+ int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth());
+ return nMinKashida;
+}
+
+// =======================================================================
+
+ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD )
+: ImplFontEntry( rFSD )
+, maWidthMap( 512 )
+, mpKerningPairs( NULL )
+, mnKerningPairs( -1 )
+, mnMinKashidaWidth( -1 )
+, mnMinKashidaGlyph( -1 )
+{
+#ifdef USE_UNISCRIBE
+ maScriptCache = NULL;
+#endif // USE_UNISCRIBE
+}
+
+// -----------------------------------------------------------------------
+
+ImplWinFontEntry::~ImplWinFontEntry()
+{
+#ifdef USE_UNISCRIBE
+ if( maScriptCache != NULL )
+ (*pScriptFreeCache)( &maScriptCache );
+#endif // USE_UNISCRIBE
+#ifdef GCP_KERN_HACK
+ delete[] mpKerningPairs;
+#endif // GCP_KERN_HACK
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontEntry::HasKernData() const
+{
+ return (mnKerningPairs >= 0);
+}
+
+// -----------------------------------------------------------------------
+
+void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
+{
+ mnKerningPairs = nPairCount;
+ mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
+ ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
+}
+
+// -----------------------------------------------------------------------
+
+int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+ int nKernAmount = 0;
+ if( mpKerningPairs )
+ {
+ const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
+ const KERNINGPAIR* pFirstPair = mpKerningPairs;
+ const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
+ const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
+ pEndPair, aRefPair, ImplCmpKernData );
+ if( (pPair != pEndPair)
+ && (pPair->wFirst == aRefPair.wFirst)
+ && (pPair->wSecond == aRefPair.wSecond) )
+ nKernAmount = pPair->iKernAmount;
+ }
+
+ return nKernAmount;
+}
+
+// -----------------------------------------------------------------------
+
+bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
+{
+ if( mnMinKashidaWidth >= 0 ) // already cached?
+ return mnMinKashidaWidth;
+
+ // initialize the kashida width
+ mnMinKashidaWidth = 0;
+ mnMinKashidaGlyph = 0;
+#ifdef USE_UNISCRIBE
+ if (aUspModule || (bUspEnabled && InitUSP()))
+ {
+ SCRIPT_FONTPROPERTIES aFontProperties;
+ aFontProperties.cBytes = sizeof (aFontProperties);
+ SCRIPT_CACHE& rScriptCache = GetScriptCache();
+ HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties );
+ if( nRC != 0 )
+ return false;
+ mnMinKashidaWidth = aFontProperties.iKashidaWidth;
+ mnMinKashidaGlyph = aFontProperties.wgKashida;
+ }
+#endif // USE_UNISCRIBE
+
+ return true;
+}
+
+// =======================================================================
+
+ImplFontData* ImplWinFontData::Clone() const
+{
+ if( mpUnicodeMap )
+ mpUnicodeMap->AddReference();
+ ImplFontData* pClone = new ImplWinFontData( *this );
+ return pClone;
+}
+
+// -----------------------------------------------------------------------
+
+ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
+{
+ ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD );
+ return pEntry;
+}
+
+// =======================================================================