vcl/inc/win/salgdi.h | 39 vcl/inc/win/winlayout.hxx | 161 -- vcl/win/gdi/salfont.cxx | 129 - vcl/win/gdi/salgdi.cxx | 5 vcl/win/gdi/winlayout.cxx | 3173 +--------------------------------------------- 5 files changed, 132 insertions(+), 3375 deletions(-)
New commits: commit 6c436ba09cb35235ce6f4065cf74c9a6ff14a4bd Author: Khaled Hosny <khaledho...@eglug.org> Date: Thu Dec 1 03:33:30 2016 +0200 Kill old Windows layout engines Change-Id: I33f8322a6371150698bf926165fb6dddb9d4092c Reviewed-on: https://gerrit.libreoffice.org/31452 Tested-by: Jenkins <c...@libreoffice.org> Reviewed-by: Khaled Hosny <khaledho...@eglug.org> diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h index 8471699..d3a7185 100644 --- a/vcl/inc/win/salgdi.h +++ b/vcl/inc/win/salgdi.h @@ -74,27 +74,16 @@ public: BYTE GetCharSet() const { return meWinCharSet; } BYTE GetPitchAndFamily() const { return mnPitchAndFamily; } - bool SupportsCJK() const { return mbHasCJKSupport; } - bool SupportsArabic() const { return mbHasArabicSupport; } FontCharMapRef GetFontCharMap() const; bool GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const; - const Ucs2SIntMap* GetEncodingVector() const { return mpEncodingVector; } - void SetEncodingVector( const Ucs2SIntMap* pNewVec ) const - { - if( mpEncodingVector ) - delete mpEncodingVector; - mpEncodingVector = pNewVec; - } + private: sal_IntPtr mnId; // some members that are initalized lazily when the font gets selected into a HDC - mutable bool mbHasCJKSupport; - mutable bool mbHasArabicSupport; mutable bool mbFontCapabilitiesRead; mutable FontCharMapRef mxUnicodeMap; - mutable const Ucs2SIntMap* mpEncodingVector; mutable vcl::FontCapabilities maFontCapabilities; BYTE meWinCharSet; @@ -105,14 +94,8 @@ private: void ReadCmapTable( HDC ) const; void GetFontCapabilities( HDC hDC ) const; - void ReadGsubTable( HDC ) const; - - mutable std::unordered_set<sal_UCS4> maGsubTable; - mutable bool mbGsubRead; mutable hb_font_t* mpHbFont; public: - bool HasGSUBstitutions( HDC ) const; - bool IsGSUBstituted( sal_UCS4 ) const; hb_font_t* GetHbFont() const { return mpHbFont; } void SetHbFont( hb_font_t* pHbFont ) const { mpHbFont = pHbFont; } }; @@ -168,9 +151,6 @@ class WinSalGraphics : public SalGraphics friend class WinOpenGLSalGraphicsImpl; friend class ScopedFont; friend class OpenGLCompatibleDC; - friend class WinLayout; - friend class SimpleWinLayout; - friend class UniscribeLayout; protected: std::unique_ptr<SalGraphicsImpl> mpImpl; @@ -196,9 +176,6 @@ private: COLORREF mnTextColor; // TextColor RGNDATA* mpClipRgnData; // ClipRegion-Data RGNDATA* mpStdClipRgnData; // Cache Standard-ClipRegion-Data - bool mbFontKernInit; // FALSE: FontKerns must be queried - KERNINGPAIR* mpFontKernPairs; // Kerning Pairs of the current Font - sal_uIntPtr mnFontKernPairCount;// Number of Kerning Pairs of the current Font int mnPenWidth; // Linienbreite LogicalFontInstance* GetWinFontEntry(int nFallbackLevel); @@ -321,9 +298,6 @@ protected: private: // local helpers - // get kernign pairs of the current font - sal_uLong GetKernPairs(); - static void DrawTextLayout(const CommonSalLayout&, HDC, bool bUseDWrite); public: @@ -427,17 +401,6 @@ void ImplGetLogFontFromFontSelect( HDC, const FontSelectPattern*, #define MAX_64KSALPOINTS ((((sal_uInt16)0xFFFF)-8)/sizeof(POINTS)) -// #102411# Win's GCP mishandles kerning => we need to do it ourselves -// SalGraphicsData::mpFontKernPairs is sorted by -inline bool ImplCmpKernData( const KERNINGPAIR& a, const KERNINGPAIR& b ) -{ - if( a.wFirst < b.wFirst ) - return true; - if( a.wFirst > b.wFirst ) - return false; - return (a.wSecond < b.wSecond); -} - // called extremely often from just one spot => inline inline bool WinFontFace::HasChar( sal_uInt32 cChar ) const { diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx index ff57506..d339df3 100644 --- a/vcl/inc/win/winlayout.hxx +++ b/vcl/inc/win/winlayout.hxx @@ -35,7 +35,6 @@ typedef std::unordered_map<int,int> IntMap; class WinFontInstance; -struct VisualItem; namespace { @@ -53,7 +52,6 @@ struct OpenGLGlyphDrawElement int mnBaselineOffset; int mnHeight; bool mbVertical; - bool mbRealGlyphIndices; int getExtraSpace() const { @@ -160,24 +158,12 @@ private: // TODO: also add HFONT??? Watch out for issues with too many active fonts... public: - bool HasKernData() const; - void SetKernData( int, const KERNINGPAIR* ); - int GetKerning( sal_Unicode, sal_Unicode ) const; - -private: - KERNINGPAIR* mpKerningPairs; - int mnKerningPairs; - -public: SCRIPT_CACHE& GetScriptCache() const { return maScriptCache; } private: mutable SCRIPT_CACHE maScriptCache; 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; } @@ -185,7 +171,7 @@ public: private: GlyphCache maGlyphCache; public: - bool CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics); + bool CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex, SalGraphics& rGraphics); GlyphCache& GetGlyphCache() { @@ -193,155 +179,10 @@ public: } private: - std::unordered_map<int, int> maWidthMap; mutable int mnMinKashidaWidth; mutable int mnMinKashidaGlyph; }; -class WinLayout : public SalLayout -{ -public: - WinLayout(HDC, const WinFontFace&, WinFontInstance&, bool bUseOpenGL); - virtual ~WinLayout() override; - virtual void InitFont() const override; - void SetFontScale( float f ) { mfFontScale = f; } - HFONT DisableFontScaling() const; - - SCRIPT_CACHE& GetScriptCache() const; - - /// In the non-OpenGL case, call the DrawTextImpl directly, otherwise make - /// sure we draw to an interim texture. - virtual void DrawText(SalGraphics&) const override; - - /// Draw to the provided HDC. - virtual bool DrawTextImpl(HDC hDC, const Rectangle* pRectToErase, Point* pPos, int* pGetNextGlypInfo) const = 0; - - virtual bool CacheGlyphs(SalGraphics& rGraphics) const = 0; - virtual bool DrawCachedGlyphs(SalGraphics& rGraphics) const = 0; - - 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 - bool mbUseOpenGL; ///< We need to render via OpenGL - - const WinFontFace& mrWinFontData; - WinFontInstance& mrWinFontEntry; -}; - -class SimpleWinLayout : public WinLayout -{ -public: - SimpleWinLayout(HDC, const WinFontFace&, WinFontInstance&, bool bUseOpenGL); - virtual ~SimpleWinLayout() override; - - virtual bool LayoutText( ImplLayoutArgs& ) override; - virtual void AdjustLayout( ImplLayoutArgs& ) override; - virtual bool DrawTextImpl(HDC hDC, const Rectangle* pRectToErase, Point* pPos, int* pGetNextGlypInfo) const override; - - virtual bool CacheGlyphs(SalGraphics& rGraphics) const override; - virtual bool DrawCachedGlyphs(SalGraphics& rGraphics) const override; - virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, - DeviceCoordinate* pGlyphAdvances = nullptr, int* pCharIndexes = nullptr, - const PhysicalFontFace** pFallbackFonts = nullptr ) const override; - - virtual DeviceCoordinate FillDXArray( DeviceCoordinate* pDXArray ) const override; - virtual sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override; - virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const override; - - // for glyph+font+script fallback - virtual void MoveGlyph( int nStart, long nNewXPos ) override; - virtual void DropGlyph( int nStart ) override; - virtual void Simplify( bool bIsBase ) override; - -protected: - void Justify( DeviceCoordinate 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; - - int mnNotdefWidth; -}; - -class UniscribeLayout : public WinLayout -{ -public: - UniscribeLayout(HDC, const WinFontFace&, WinFontInstance&, bool bUseOpenGL); - - virtual bool LayoutText( ImplLayoutArgs& ) override; - virtual void AdjustLayout( ImplLayoutArgs& ) override; - virtual bool DrawTextImpl(HDC hDC, const Rectangle* pRectToErase, Point* pPos, int* pGetNextGlypInfo) const override; - virtual bool CacheGlyphs(SalGraphics& rGraphics) const override; - virtual bool DrawCachedGlyphs(SalGraphics& rGraphics) const override; - virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, - DeviceCoordinate* pGlyphAdvances = nullptr, int* pCharPosAry = nullptr, - const PhysicalFontFace** pFallbackFonts = nullptr ) const override; - - virtual DeviceCoordinate FillDXArray( DeviceCoordinate* pDXArray ) const override; - virtual sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override; - virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const override; - virtual bool IsKashidaPosValid ( int nCharPos ) const override; - - // for glyph+font+script fallback - virtual void MoveGlyph( int nStart, long nNewXPos ) override; - virtual void DropGlyph( int nStart ) override; - virtual void Simplify( bool bIsBase ) override; - virtual void DisableGlyphInjection( bool bDisable ) override { mbDisableGlyphInjection = bDisable; } - -protected: - virtual ~UniscribeLayout() override; - - void Justify( DeviceCoordinate 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 - OUString msTheString; // Sadly we need it in GetNextGlyphs(), to be able to call GetVerticalFlags() - 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; -}; - class TextOutRenderer { protected: diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx index 5d1e6bc..7147be1 100644 --- a/vcl/win/gdi/salfont.cxx +++ b/vcl/win/gdi/salfont.cxx @@ -613,16 +613,12 @@ WinFontFace::WinFontFace( const FontAttributes& rDFS, int nHeight, BYTE eWinCharSet, BYTE nPitchAndFamily ) : PhysicalFontFace( rDFS ), mnId( 0 ), - mbHasCJKSupport( false ), - mbHasArabicSupport ( false ), mbFontCapabilitiesRead( false ), mxUnicodeMap( nullptr ), - mpEncodingVector( nullptr ), meWinCharSet( eWinCharSet ), mnPitchAndFamily( nPitchAndFamily ), mbAliasSymbolsHigh( false ), mbAliasSymbolsLow( false ), - mbGsubRead( false ), mpHbFont( nullptr ) { SetBitmapSize( 0, nHeight ); @@ -657,8 +653,6 @@ WinFontFace::~WinFontFace() if( mxUnicodeMap.Is() ) mxUnicodeMap = nullptr; - delete mpEncodingVector; - if( mpHbFont ) hb_font_destroy( mpHbFont ); } @@ -681,18 +675,6 @@ void WinFontFace::UpdateFromHDC( HDC hDC ) const GetFontCapabilities( hDC ); } -bool WinFontFace::HasGSUBstitutions( HDC hDC ) const -{ - if( !mbGsubRead ) - ReadGsubTable( hDC ); - return !maGsubTable.empty(); -} - -bool WinFontFace::IsGSUBstituted( sal_UCS4 cChar ) const -{ - return( maGsubTable.find( cChar ) != maGsubTable.end() ); -} - FontCharMapRef WinFontFace::GetFontCharMap() const { return mxUnicodeMap; @@ -704,51 +686,6 @@ bool WinFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange; } -void WinFontFace::ReadGsubTable( HDC hDC ) const -{ - mbGsubRead = true; - - // check the existence of a GSUB table - const DWORD GsubTag = CalcTag( "GSUB" ); - DWORD nRC = ::GetFontData( hDC, GsubTag, 0, nullptr, 0 ); - if( (nRC == GDI_ERROR) || !nRC ) - return; - - // parse the GSUB table through sft - // TODO: parse it directly - - // sft needs the full font file data => get it - const RawFontData aRawFontData( hDC ); - if( !aRawFontData.get() ) - return; - - // open font file - sal_uInt32 nFaceNum = 0; - if( !*aRawFontData.get() ) // TTC candidate - nFaceNum = ~0U; // indicate "TTC font extracts only" - - TrueTypeFont* pTTFont = nullptr; - ::OpenTTFontBuffer( aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont ); - if( !pTTFont ) - return; - - // add vertically substituted characters to list - static const sal_Unicode aGSUBCandidates[] = { - 0x0020, 0x0080, // ASCII - 0x2000, 0x2600, // misc - 0x3000, 0x3100, // CJK punctutation - 0x3300, 0x3400, // squared words - 0xFF00, 0xFFF0, // halfwidth|fullwidth forms - 0 }; - - for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 ) - for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar ) - if( ::MapChar( pTTFont, cChar, false ) != ::MapChar( pTTFont, cChar, true ) ) - maGsubTable.insert( cChar ); // insert GSUBbed unicodes - - CloseTTFont( pTTFont ); -} - void WinFontFace::ReadCmapTable( HDC hDC ) const { if( mxUnicodeMap.Is() ) @@ -784,37 +721,15 @@ void WinFontFace::GetFontCapabilities( HDC hDC ) const mbFontCapabilitiesRead = true; - // GSUB table - DWORD nLength; - const DWORD GsubTag = CalcTag( "GSUB" ); - nLength = ::GetFontData( hDC, GsubTag, 0, nullptr, 0 ); - if( (nLength != GDI_ERROR) && nLength ) - { - std::vector<unsigned char> aTable( nLength ); - unsigned char* pTable = &aTable[0]; - ::GetFontData( hDC, GsubTag, 0, pTable, nLength ); - vcl::getTTScripts(maFontCapabilities.maGSUBScriptTags, pTable, nLength); - } - // OS/2 table const DWORD OS2Tag = CalcTag( "OS/2" ); - nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 ); + DWORD nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 ); if( (nLength != GDI_ERROR) && nLength ) { std::vector<unsigned char> aTable( nLength ); unsigned char* pTable = &aTable[0]; ::GetFontData( hDC, OS2Tag, 0, pTable, nLength ); - if (vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength)) - { - // Check for CJK capabilities of the current font - // TODO, we have this info already from getTT, decode bits to - // a readable bitset - sal_uInt32 ulUnicodeRange1 = GetUInt( pTable + 42 ); - sal_uInt32 ulUnicodeRange2 = GetUInt( pTable + 46 ); - - mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000); - mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000); - } + vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength); } } @@ -1051,17 +966,6 @@ void WinSalGraphics::SetFont( FontSelectPattern* pFont, int nFallbackLevel ) // now the font is live => update font face if( mpWinFontData[ nFallbackLevel ] ) mpWinFontData[ nFallbackLevel ]->UpdateFromHDC( getHDC() ); - - if( !nFallbackLevel ) - { - mbFontKernInit = TRUE; - if ( mpFontKernPairs ) - { - delete[] mpFontKernPairs; - mpFontKernPairs = nullptr; - } - mnFontKernPairCount = 0; - } } void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel ) @@ -1106,35 +1010,6 @@ void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFa rxFontMetric->SetMinKashida( GetMinKashidaWidth() ); } -sal_uLong WinSalGraphics::GetKernPairs() -{ - if ( mbFontKernInit ) - { - if( mpFontKernPairs ) - { - delete[] mpFontKernPairs; - mpFontKernPairs = nullptr; - } - mnFontKernPairCount = 0; - - KERNINGPAIR* pPairs = nullptr; - int nCount = ::GetKerningPairsW( getHDC(), 0, nullptr ); - if( nCount ) - { - pPairs = new KERNINGPAIR[ nCount+1 ]; - mpFontKernPairs = pPairs; - mnFontKernPairCount = nCount; - ::GetKerningPairsW( getHDC(), nCount, pPairs ); - } - - mbFontKernInit = FALSE; - - std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData ); - } - - return mnFontKernPairCount; -} - const FontCharMapRef WinSalGraphics::GetFontCharMap() const { if( !mpWinFontData[0] ) diff --git a/vcl/win/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx index 2956fed..4c63eea 100644 --- a/vcl/win/gdi/salgdi.cxx +++ b/vcl/win/gdi/salgdi.cxx @@ -615,9 +615,6 @@ WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hW mhDefFont(nullptr), mhDefPal(nullptr), mpStdClipRgnData(nullptr), - mbFontKernInit(false), - mpFontKernPairs(nullptr), - mnFontKernPairCount(0), mnPenWidth(GSL_PEN_WIDTH) { if (OpenGLHelper::isVCLOpenGLEnabled() && !mbPrinter) @@ -647,8 +644,6 @@ WinSalGraphics::~WinSalGraphics() // delete cache data delete [] reinterpret_cast<BYTE*>(mpStdClipRgnData); - - delete [] mpFontKernPairs; } SalGraphicsImpl* WinSalGraphics::GetImpl() const diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx index e2ea865..621c241 100644 --- a/vcl/win/gdi/winlayout.cxx +++ b/vcl/win/gdi/winlayout.cxx @@ -37,7 +37,6 @@ #include <cstdio> #include <cstdlib> -#include <sal/alloca.h> #include <rtl/character.hxx> #include <algorithm> @@ -45,3010 +44,187 @@ #include <shlwapi.h> #include <winver.h> -#include <unordered_map> -#include <unordered_set> - -#define DROPPED_OUTGLYPH 0xFFFF - // static initialization std::unique_ptr<GlobalGlyphCache> GlyphCache::gGlobalGlyphCache(new GlobalGlyphCache); -inline void WinFontInstance::CacheGlyphWidth( int nCharCode, int nCharWidth ) -{ - maWidthMap[ nCharCode ] = nCharWidth; -} - -inline int WinFontInstance::GetCachedGlyphWidth( int nCharCode ) const -{ - auto it = maWidthMap.find( nCharCode ); - if( it == maWidthMap.end() ) - return -1; - return it->second; -} - -bool WinFontInstance::CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics) +bool WinFontInstance::CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex, SalGraphics& rGraphics) { - if (nGlyphIndex == DROPPED_OUTGLYPH) - return true; - OpenGLGlyphDrawElement aElement; - aElement.mbRealGlyphIndices = bRealGlyphIndices; - - std::vector<uint32_t> aCodePointsOrGlyphIndices(1); - aCodePointsOrGlyphIndices[0] = nGlyphIndex; - HDC hDC = CreateCompatibleDC(rLayout.mhDC); - if (hDC == nullptr) + HDC hNewDC = CreateCompatibleDC(hDC); + if (hNewDC == nullptr) { SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError())); return false; } - HFONT hOrigFont = static_cast<HFONT>(SelectObject(hDC, rLayout.mhFont)); - if (hOrigFont == nullptr) - { - SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError())); - DeleteDC(hDC); - return false; - } - - // For now we assume DWrite is present and we won't bother with fallback paths. - D2DWriteTextOutRenderer * pTxt = dynamic_cast<D2DWriteTextOutRenderer *>(&TextOutRenderer::get(true)); - if (!pTxt) - return false; - - if (!pTxt->BindFont(hDC)) - { - SAL_WARN("vcl.gdi", "Binding of font failed. The font might not be supported by Direct Write."); - DeleteDC(hDC); - return false; - } - - // Bail for non-horizontal text. - { - wchar_t sFaceName[200]; - int nFaceNameLen = GetTextFaceW(hDC, SAL_N_ELEMENTS(sFaceName), sFaceName); - - if (!nFaceNameLen) - SAL_WARN("vcl.gdi", "GetTextFace failed: " << WindowsErrorString(GetLastError())); - - LOGFONTW aLogFont; - GetObjectW(rLayout.mhFont, sizeof(LOGFONTW), &aLogFont); - - SelectObject(hDC, hOrigFont); - DeleteDC(hDC); - - if (sFaceName[0] == '@' || aLogFont.lfOrientation != 0 || aLogFont.lfEscapement != 0) - { - pTxt->ReleaseFont(); - return false; - } - } - std::vector<WORD> aGlyphIndices(1); - // Fetch the ink boxes and calculate the size of the atlas. - if (!bRealGlyphIndices) - { - if (!pTxt->GetFontFace()) - { - SAL_WARN("vcl.gdi", "Font face is not available."); - return false; - } - if (!SUCCEEDED(pTxt->GetFontFace()->GetGlyphIndices(aCodePointsOrGlyphIndices.data(), aCodePointsOrGlyphIndices.size(), aGlyphIndices.data()))) - { - pTxt->ReleaseFont(); - return false; - } - } - else - { - aGlyphIndices[0] = aCodePointsOrGlyphIndices[0]; - } - - Rectangle bounds(0, 0, 0, 0); - auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1); - for (auto &box : aInkBoxes) - bounds.Union(box + Point(bounds.Right(), 0)); - - // bounds.Top() is the offset from the baseline at (0,0) to the top of the - // inkbox. - aElement.mnBaselineOffset = -bounds.Top(); - aElement.mnHeight = bounds.getHeight(); - aElement.mbVertical = false; - - // Try hard to avoid overlap as we want to be able to use - // individual rectangles for each glyph. The ABC widths don't - // take anti-aliasing into consideration. Let's hope that leaving - // "extra" space between glyphs will help. - std::vector<float> aGlyphAdv(1); // offsets between glyphs - std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, DWRITE_GLYPH_OFFSET{0.0f, 0.0f}); - std::vector<int> aEnds(1); // end of each glyph box - float totWidth = 0; - { - int overhang = aInkBoxes[0].Left(); - int blackWidth = aInkBoxes[0].getWidth(); // width of non-AA pixels - aElement.maLeftOverhangs = overhang; - - aGlyphAdv[0] = blackWidth + aElement.getExtraSpace(); - aGlyphOffset[0].advanceOffset = -overhang; - - totWidth += aGlyphAdv[0]; - aEnds[0] = totWidth; - } - // Leave extra space also at top and bottom - int nBitmapWidth = totWidth; - int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace(); - - UINT nPos = 0; - - // FIXME: really I don't get why 'vertical' makes any difference [!] what does it mean !? - if (aElement.mbVertical) - { - aElement.maLocation.Left() = 0; - aElement.maLocation.Right() = nBitmapWidth; - aElement.maLocation.Top() = nPos; - aElement.maLocation.Bottom() = nPos + aGlyphAdv[0] + aElement.maLeftOverhangs; - } - else - { - aElement.maLocation.Left() = nPos; - aElement.maLocation.Right() = aEnds[0]; - aElement.maLocation.Top() = 0; - aElement.maLocation.Bottom() = bounds.getHeight() + aElement.getExtraSpace(); - } - nPos = aEnds[0]; - - OpenGLCompatibleDC aDC(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight); - - HFONT hNonAntialiasedFont = nullptr; - - SetTextColor(aDC.getCompatibleHDC(), RGB(0, 0, 0)); - SetBkColor(aDC.getCompatibleHDC(), RGB(255, 255, 255)); - - aDC.fill(MAKE_SALCOLOR(0xff, 0xff, 0xff)); - - pTxt->BindDC(aDC.getCompatibleHDC(), Rectangle(0, 0, nBitmapWidth, nBitmapHeight)); - auto pRT = pTxt->GetRenderTarget(); - - ID2D1SolidColorBrush* pBrush = nullptr; - if (!SUCCEEDED(pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush))) - { - pTxt->ReleaseFont(); - return false; - } - - D2D1_POINT_2F baseline = { - aElement.getExtraOffset(), - aElement.getExtraOffset() + aElement.mnBaselineOffset - }; - - DWRITE_GLYPH_RUN glyphs = { - pTxt->GetFontFace(), - pTxt->GetEmHeight(), - 1, - aGlyphIndices.data(), - aGlyphAdv.data(), - aGlyphOffset.data(), - false, - 0 - }; - - pRT->BeginDraw(); - pRT->DrawGlyphRun(baseline, &glyphs, pBrush); - HRESULT hResult = pRT->EndDraw(); - - pBrush->Release(); - - switch (hResult) - { - case S_OK: - break; - case D2DERR_RECREATE_TARGET: - pTxt->CreateRenderTarget(); - break; - default: - SAL_WARN("vcl.gdi", "DrawGlyphRun-EndDraw failed: " << WindowsErrorString(GetLastError())); - SelectFont(aDC.getCompatibleHDC(), hOrigFont); - if (hNonAntialiasedFont != nullptr) - DeleteObject(hNonAntialiasedFont); - return false; - } - - pTxt->ReleaseFont(); - - if (!GlyphCache::ReserveTextureSpace(aElement, nBitmapWidth, nBitmapHeight)) - return false; - if (!aDC.copyToTexture(aElement.maTexture)) - return false; - - maGlyphCache.PutDrawElementInCache(aElement, nGlyphIndex); - - SelectFont(aDC.getCompatibleHDC(), hOrigFont); - if (hNonAntialiasedFont != nullptr) - DeleteObject(hNonAntialiasedFont); - - return true; -} - -SimpleWinLayout::SimpleWinLayout(HDC hDC, const WinFontFace& rWinFontData, - WinFontInstance& rWinFontEntry, bool bUseOpenGL) -: WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL), - mnGlyphCount( 0 ), - mnCharCount( 0 ), - mpOutGlyphs( nullptr ), - mpGlyphAdvances( nullptr ), - mpGlyphOrigAdvs( nullptr ), - mpCharWidths( nullptr ), - mpChars2Glyphs( nullptr ), - mpGlyphs2Chars( nullptr ), - mpGlyphRTLFlags( nullptr ), - mnWidth( 0 ), - mnNotdefWidth( -1 ) -{ -} - -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 - mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; - - // TODO: use a cached value for bDisableAsianKern from upper layers - if( rArgs.mnFlags & SalLayoutFlags::KerningAsian ) - { - TEXTMETRICA aTextMetricA; - if( GetTextMetricsA( mhDC, &aTextMetricA ) - && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) ) - rArgs.mnFlags &= ~SalLayoutFlags::KerningAsian; - } - - // layout text - int i, j; - - mnGlyphCount = 0; - bool bVertical(rArgs.mnFlags & SalLayoutFlags::Vertical); - - // 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.mrStr.pData->buffer + 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 = static_cast<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.mrStr[ nCharPos ]; - - // in the RTL case mirror the character and remember its RTL status - if( bIsRTL ) - { - cChar = GetMirroredChar( cChar ); - mpGlyphRTLFlags[ mnGlyphCount ] = true; - } - - // 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 & (SalLayoutFlags::KerningPairs | SalLayoutFlags::KerningAsian) ) - mpGlyphOrigAdvs = new int[ mnGlyphCount ]; - - 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 ) - { - // ignore high surrogates, they were already processed with their low surrogates - if( nCharCode >= 0xDC00 ) - continue; - // check the second half of the surrogate pair - bSurrogate &= (0xDC00 <= pCodes[1]) && (pCodes[1] <= 0xDFFF); - // calculate the UTF-32 code of valid surrogate pairs - if( bSurrogate ) - nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00); - else // or fall back to a replacement character - { - // FIXME: Surely this is an error situation that should not happen? - nCharCode = '?'; - } - } - - int nGlyphWidth = 0; - // Unicode variance selectors selects glyph of previous base character, do not have width itself. - if ( (nCharCode >= 0xFE00 && nCharCode <= 0xFE0F) || (nCharCode >= 0xE0100 && nCharCode <= 0xE01EF) ) - { - mpOutGlyphs[ i ] = DROPPED_OUTGLYPH; - mpGlyphAdvances[ i ] = 0; - if ( bSurrogate && ( i+1 ) < mnGlyphCount ) - { - mpOutGlyphs[ ++i ] = DROPPED_OUTGLYPH; - mpGlyphAdvances[ i ] = 0; - } - continue; - } - else - { - // get the advance width for the current UTF-32 code point - 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; - - // the second half of surrogate pair gets 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]; - int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos; - rArgs.NeedFallback( nCharPos, bRTL ); - if( bSurrogate && ((nCharPos+1) < rArgs.mrStr.getLength()) ) - rArgs.NeedFallback( nCharPos+1, bRTL ); - - // replace the current glyph shape with the NotDef glyph shape - if( rArgs.mnFlags & SalLayoutFlags::ForFallback ) - { - // 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.mrStr[ nCharPos ]; - mnNotdefWidth = 0; - if( GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) ) - mnNotdefWidth = aExtent.cx; - } - } - 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; - } - - // apply kerning if the layout engine has not yet done it - if( rArgs.mnFlags & (SalLayoutFlags::KerningAsian|SalLayoutFlags::KerningPairs) ) - { - 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.mrStr.getLength() ) - ++nLen; - for( i = 1; i < nLen; ++i ) - { - if( rArgs.mnFlags & SalLayoutFlags::KerningPairs ) - { - int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] ); - mpGlyphAdvances[ i-1 ] += nKernAmount; - mnWidth += nKernAmount; - } - else if( rArgs.mnFlags & SalLayoutFlags::KerningAsian ) - - 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 k = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos; - if( k >= 0 ) - mpCharWidths[ k ] += 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* pGlyphIds, Point& rPos, int& nStart, - DeviceCoordinate* pGlyphAdvances, int* pCharIndexes, - const PhysicalFontFace** /*pFallbackFonts*/ ) 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 {aGlyphId,nCharPos,nGlyphAdvance} - sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ]; - if( mnLayoutFlags & SalLayoutFlags::Vertical ) - { - const sal_UCS4 cChar = static_cast<sal_UCS4>(aGlyphId & GF_IDXMASK); - if( mrWinFontData.HasGSUBstitutions( mhDC ) - && mrWinFontData.IsGSUBstituted( cChar ) ) - aGlyphId |= GF_GSUB | GF_ROTL; - else - { - aGlyphId |= GetVerticalFlags( cChar ); - if( (aGlyphId & GF_ROTMASK) == 0 ) - aGlyphId |= GF_VERT; - } - } - aGlyphId |= GF_ISCHAR; - - ++nCount; - *(pGlyphIds++) = aGlyphId; - 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; -} - -bool SimpleWinLayout::DrawTextImpl(HDC hDC, - const Rectangle* /* pRectToErase */, - Point* /* pPos */, - int* /* pGetNextGlypInfo */) const -{ - if( mnGlyphCount <= 0 ) - return false; - - HFONT hOrigFont = DisableFontScaling(); - Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) ); - - // #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) - POINT oldPos; - UINT oldTa = GetTextAlign(hDC); - SetTextAlign(hDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP); - MoveToEx(hDC, aPos.X(), aPos.Y(), &oldPos); - unsigned int i = 0; - for( unsigned int n = 0; n < numGlyphPortions; ++n, i+=maxGlyphCount ) - { - ExtTextOutW(hDC, 0, 0, 0, nullptr, mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i); - } - ExtTextOutW(hDC, 0, 0, 0, nullptr, mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i); - MoveToEx(hDC, oldPos.x, oldPos.y, nullptr); - SetTextAlign(hDC, oldTa); - } - else - ExtTextOutW(hDC, aPos.X(), aPos.Y(), 0, nullptr, mpOutGlyphs, mnGlyphCount, mpGlyphAdvances); - - if( hOrigFont ) - DeleteFont(SelectFont(hDC, hOrigFont)); - - return false; -} - -DeviceCoordinate SimpleWinLayout::FillDXArray( DeviceCoordinate* pDXArray ) const -{ - if( !mnWidth ) - { - mnWidth = mnBaseAdv; - for( int i = 0; i < mnGlyphCount; ++i ) - mnWidth += mpGlyphAdvances[ i ]; - } - - if( pDXArray != nullptr ) - { - for( int i = 0; i < mnCharCount; ++i ) - pDXArray[ i ] = mpCharWidths[ i ]; - } - - return mnWidth; -} - -sal_Int32 SimpleWinLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate 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 -1; - - 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 -1; -} - -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( DeviceCoordinate nNewWidth ) -{ - DeviceCoordinate 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 - DeviceCoordinate nDiffWidth = nNewWidth - nOldWidth; - for( i = 0; (i < nRight) && (nStretchable > 0); ++i ) - { - if( mpGlyphAdvances[i] <= 0 ) - continue; - DeviceCoordinate 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 ]; - long 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; -} - -WinLayout::WinLayout(HDC hDC, const WinFontFace& rWFD, WinFontInstance& rWFE, bool bUseOpenGL) -: mhDC( hDC ), - mhFont( static_cast<HFONT>(GetCurrentObject(hDC,OBJ_FONT)) ), - mnBaseAdv( 0 ), - mfFontScale( 1.0 ), - mbUseOpenGL(bUseOpenGL), - mrWinFontData( rWFD ), - mrWinFontEntry(rWFE) -{ - assert(mrWinFontEntry.mnRefCount > 0); - // keep mrWinFontEntry alive - mrWinFontEntry.mpFontCache->Acquire(&mrWinFontEntry); -} - -WinLayout::~WinLayout() -{ - mrWinFontEntry.mpFontCache->Release(&mrWinFontEntry); -} - -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 nullptr; - - LOGFONTW aLogFont; - GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont); - aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight); - aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth); - HFONT hHugeFont = CreateFontIndirectW( &aLogFont); - if( !hHugeFont ) - return nullptr; - - return SelectFont( mhDC, hHugeFont ); -} - -SCRIPT_CACHE& WinLayout::GetScriptCache() const -{ - return mrWinFontEntry.GetScriptCache(); -} - -void WinLayout::DrawText(SalGraphics& rGraphics) const -{ - WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics); - HDC hDC = rWinGraphics.getHDC(); - - if (!mbUseOpenGL) - { - // no OpenGL, just classic rendering - Point aPos(0, 0); - int nGetNextGlypInfo(0); - bool bContinue = DrawTextImpl(hDC, nullptr, &aPos, &nGetNextGlypInfo); - assert(!bContinue); - } - else if (CacheGlyphs(rGraphics) && - DrawCachedGlyphs(rGraphics)) - { - // Nothing - } - else - { - // We have to render the text to a hidden texture, and draw it. - // - // Note that Windows GDI does not really support the alpha correctly - // when drawing - ie. it draws nothing to the alpha channel when - // rendering the text, even the antialiasing is done as 'real' pixels, - // not alpha... - // - // Luckily, this does not really limit us: - // - // To blend properly, we draw the texture, but then use it as an alpha - // channel for solid color (that will define the text color). This - // destroys the subpixel antialiasing - turns it into 'classic' - // antialiasing - but that is the best we can do, because the subpixel - // antialiasing needs to know what is in the background: When the - // background is white, or white-ish, it does the subpixel, but when - // there is a color, it just darkens the color (and does this even - // when part of the character is on a colored background, and part on - // white). It has to work this way, the results would look strange - // otherwise. - // - // For the GL rendering to work even with the subpixel antialiasing, - // we would need to get the current texture from the screen, let GDI - // draw the text to it (so that it can decide well where to use the - // subpixel and where not), and draw the result - but in that case we - // don't need alpha anyway. - // - // TODO: check the performance of this 2nd approach at some stage and - // switch to that if it performs well. - - Rectangle aRect; - GetBoundRect(rGraphics, aRect); - - WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get()); - - if (pImpl) - { - pImpl->PreDraw(); - - Point aPos(0, 0); - int nGetNextGlypInfo(0); - while (true) - { - OpenGLCompatibleDC aDC(rGraphics, aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight()); - - // we are making changes to the DC, make sure we got a new one - assert(aDC.getCompatibleHDC() != hDC); - - RECT aWinRect = { aRect.Left(), aRect.Top(), aRect.Left() + aRect.GetWidth(), aRect.Top() + aRect.GetHeight() }; - FillRect(aDC.getCompatibleHDC(), &aWinRect, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH))); - - // setup the hidden DC with black color and white background, we will - // use the result of the text drawing later as a mask only - HFONT hOrigFont = SelectFont(aDC.getCompatibleHDC(), mhFont); - - SetTextColor(aDC.getCompatibleHDC(), RGB(0, 0, 0)); - SetBkColor(aDC.getCompatibleHDC(), RGB(255, 255, 255)); - - UINT nTextAlign = GetTextAlign(hDC); - SetTextAlign(aDC.getCompatibleHDC(), nTextAlign); - - COLORREF color = GetTextColor(hDC); - SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color)); - - // the actual drawing - bool bContinue = DrawTextImpl(aDC.getCompatibleHDC(), &aRect, &aPos, &nGetNextGlypInfo); - - std::unique_ptr<OpenGLTexture> xTexture(aDC.getTexture()); - if (xTexture) - pImpl->DrawMask(*xTexture, salColor, aDC.getTwoRect()); - - SelectFont(aDC.getCompatibleHDC(), hOrigFont); - - if (!bContinue) - break; - } - pImpl->PostDraw(); - } - - } -} - -bool SimpleWinLayout::CacheGlyphs(SalGraphics& rGraphics) const -{ - static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == nullptr); - - if (!bDoGlyphCaching) - return false; - - for (int i = 0; i < mnGlyphCount; i++) - { - int nCodePoint; - if (i < mnGlyphCount-1 && rtl::isHighSurrogate(mpOutGlyphs[i]) && rtl::isLowSurrogate(mpOutGlyphs[i+1])) - { -#if 1 // Don't remove the #else branch in case somebody wants to - // continue trying to figure out why sequential non-BMP glyphs - // get scribbled on top of each others if caching is used. - return false; -#else - nCodePoint = rtl::combineSurrogates(mpOutGlyphs[i], mpOutGlyphs[i+1]); - i++; -#endif - } - else - { - nCodePoint = mpOutGlyphs[i]; - } - - if (!mrWinFontEntry.GetGlyphCache().IsGlyphCached(nCodePoint)) - { - if (!mrWinFontEntry.CacheGlyphToAtlas(false, nCodePoint, *this, rGraphics)) - return false; - } - } - - return true; -} - -bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const -{ - WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics); - HDC hDC = rWinGraphics.getHDC(); - - Rectangle aRect; - GetBoundRect(rGraphics, aRect); - - COLORREF color = GetTextColor(hDC); - SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color)); - - WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get()); - if (!pImpl) - return false; - - HFONT hOrigFont = DisableFontScaling(); - Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) ); - - int nAdvance = 0; - - for (int i = 0; i < mnGlyphCount; i++) - { - if (mpOutGlyphs[i] == DROPPED_OUTGLYPH) - continue; - - int nCodePoint; - if (i < mnGlyphCount-1 && rtl::isHighSurrogate(mpOutGlyphs[i]) && rtl::isLowSurrogate(mpOutGlyphs[i+1])) - { - nCodePoint = rtl::combineSurrogates(mpOutGlyphs[i], mpOutGlyphs[i+1]); - i++; - } - else - { - nCodePoint = mpOutGlyphs[i]; - } - - OpenGLGlyphDrawElement& rElement(mrWinFontEntry.GetGlyphCache().GetDrawElement(nCodePoint)); - OpenGLTexture& rTexture = rElement.maTexture; - - if (!rTexture) - return false; - - SalTwoRect a2Rects(0, 0, - rTexture.GetWidth(), rTexture.GetHeight(), - nAdvance + aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs, - aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(), - rTexture.GetWidth(), rTexture.GetHeight()); - - pImpl->DeferredTextDraw(rTexture, salColor, a2Rects); - - nAdvance += mpGlyphAdvances[i]; - } - - if( hOrigFont ) - DeleteFont(SelectFont(hDC, hOrigFont)); - - return true; -} - -struct VisualItem -{ -public: - SCRIPT_ITEM* mpScriptItem; - int mnMinGlyphPos; - int mnEndGlyphPos; - int mnMinCharPos; - int mnEndCharPos; - 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; } -}; - -static bool bUspInited = false; - -static bool bManualCellAlign = true; - -static void InitUSP() -{ -#if _WIN32_WINNT < _WIN32_WINNT_VISTA - // get the usp10.dll version info - HMODULE usp10 = GetModuleHandle("usp10.dll"); - void *pScriptIsComplex = reinterpret_cast< void* >( GetProcAddress(usp10, "ScriptIsComplex")); - int nUspVersion = 0; - rtl_uString* pModuleURL = NULL; - osl_getModuleURLFromAddress( 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 ); - 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 ) -#endif - { - bManualCellAlign = false; - } - - bUspInited = true; -} - -UniscribeLayout::UniscribeLayout(HDC hDC, const WinFontFace& rWinFontData, - WinFontInstance& rWinFontEntry, bool bUseOpenGL) -: WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL), - mpScriptItems( nullptr ), - mpVisualItems( nullptr ), - mnItemCount( 0 ), - mnCharCapacity( 0 ), - mpLogClusters( nullptr ), - mpCharWidths( nullptr ), - mnSubStringMin( 0 ), - mnGlyphCount( 0 ), - mnGlyphCapacity( 0 ), - mpGlyphAdvances( nullptr ), - mpJustifications( nullptr ), - mpOutGlyphs( nullptr ), - mpGlyphOffsets( nullptr ), - mpVisualAttrs( nullptr ), - mpGlyphs2Chars( nullptr ), - mnMinKashidaWidth( 0 ), - 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; -} - -#if 0 // Don't remove -- useful for temporary SAL_ DEBUG when hacking on this - -namespace { - -template<typename IntegerType> -OUString IntegerArrayToString(IntegerType *pWords, int n) -{ - OUString result = "{"; - for (int i = 0; i < n; ++i) - { - if (i > 0) - result += ","; - if (i > 0 && i % 10 == 0) - result += OUString::number(i) + ":"; - result += OUString::number(pWords[i]); - } - result += "}"; - - return result; -} - -OUString GoffsetArrayToString(GOFFSET *pGoffsets, int n) -{ - OUString result = "{"; - for (int i = 0; i < n; ++i) - { - if (i > 0) - result += ","; - if (i > 0 && i % 10 == 0) - result += OUString::number(i) + ":"; - result += "(" + OUString::number(pGoffsets[i].du) + "," + OUString::number(pGoffsets[i].dv) + ")"; - } - result += "}"; - - return result; -} - -OUString VisAttrArrayToString(SCRIPT_VISATTR *pVisAttrs, int n) -{ - static const OUString JUSTIFICATION_NAME[] = { - "NONE", - "ARABIC_BLANK", - "CHARACTER", - "RESERVED1", - "BLANK", - "RESERVED2", - "RESERVED3", - "ARABIC_NORMAL", - "ARABIC_KASHIDA", - "ARABIC_ALEF", - "ARABIC_HA", - "ARABIC_RA", - "ARABIC_BA", - "ARABIC_BARA", - "ARABIC_SEEN", - "ARABIC_SEEN_M" - }; - - OUString result = "{"; - for (int i = 0; i < n; ++i) - { - if (i > 0) - result += ","; - if (i > 0 && i % 10 == 0) - result += OUString::number(i) + ":"; - result += OUString("{") + JUSTIFICATION_NAME[pVisAttrs[i].uJustification] + (pVisAttrs[i].fClusterStart ? OUString(",ClusterStart") : OUString()) + (pVisAttrs[i].fDiacritic ? OUString(",Diacritic") : OUString()) + OUString(pVisAttrs[i].fZeroWidth ? OUString(",ZeroWidth") : OUString()) + OUString("}"); - } - result += "}"; - - return result; -} - -} // anonymous namespace - -#endif // 0 - -bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs ) -{ - msTheString = rArgs.mrStr; - - // for a base layout only the context glyphs have to be dropped - // => when the whole string is involved there is no extra context - std::vector<int> aDropChars; - if( rArgs.mnFlags & SalLayoutFlags::ForFallback ) - { - // calculate superfluous context char positions - aDropChars.push_back(0); - aDropChars.push_back(rArgs.mrStr.getLength()); - 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 recycling 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,WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),WORD(false),0,0}; - aScriptState.uBidiLevel = WORD(bool(rArgs.mnFlags & SalLayoutFlags::BiDiRtl)); - aScriptState.fOverrideDirection = WORD(bool(rArgs.mnFlags & SalLayoutFlags::BiDiStrong)); - aScriptState.fDigitSubstitute = WORD(bool(rArgs.mnFlags & SalLayoutFlags::SubstituteDigits)); - aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel; - DWORD nLangId = 0; // TODO: get language from font - SCRIPT_CONTROL aScriptControl; - memset(&aScriptControl, 0, sizeof(aScriptControl)); - aScriptControl.uDefaultLanguage = nLangId; - aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection; - aScriptControl.fContextDigits = DWORD(bool(rArgs.mnFlags & SalLayoutFlags::SubstituteDigits)); - aScriptControl.fMergeNeutralItems = DWORD(true); - - // determine relevant substring and work only on it - // when Bidi status is unknown we need to look at the whole string though - mnSubStringMin = 0; - const int nLength = rArgs.mrStr.getLength(); - const sal_Unicode *pStr = rArgs.mrStr.getStr(); - int nSubStringEnd = nLength; - if( aScriptState.fOverrideDirection ) - { - // TODO: limit substring to portion limits - mnSubStringMin = rArgs.mnMinCharPos - 8; - if( mnSubStringMin < 0 ) - mnSubStringMin = 0; - nSubStringEnd = rArgs.mnEndCharPos + 8; - if( nSubStringEnd > nLength ) - nSubStringEnd = nLength; - - } - // now itemize the substring with its context - for( int nItemCapacity = 16;; nItemCapacity *= 8 ) - { - mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ]; - HRESULT nRC = ScriptItemize( - reinterpret_cast<LPCWSTR>(pStr + 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 = nullptr; - 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 & SalLayoutFlags::BiDiStrong ) - { - // force RTL item ordering if requested - if( rArgs.mnFlags & SalLayoutFlags::BiDiRtl ) - { - 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; - - // shortcut ignorable items - if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos) - || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) ) - { - for( int j = rVisualItem.mnMinCharPos; j < rVisualItem.mnEndCharPos; ++j ) - mpLogClusters[j] = sal::static_int_cast<WORD>(~0U); - if (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) - { // fdo#47553 adjust "guessed" min (maybe up to -8 off) to - // actual min so it can be used properly in GetNextGlyphs - if (mnSubStringMin < rVisualItem.mnEndCharPos) - mnSubStringMin = rVisualItem.mnEndCharPos; - } - continue; - } - - // override bidi analysis if requested - if( rArgs.mnFlags & SalLayoutFlags::BiDiStrong ) - { - // 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 = ScriptShape( mhDC, &rScriptCache, - reinterpret_cast<LPCWSTR>(pStr + 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( rArgs.mnFlags & SalLayoutFlags::ForFallback ) - continue; - - // the primitive layout engine is good enough for the default layout - rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED; - nRC = ScriptShape( mhDC, &rScriptCache, - reinterpret_cast<LPCWSTR>(pStr + rVisualItem.mnMinCharPos), - nCharCount, - mnGlyphCapacity - rVisualItem.mnMinGlyphPos, - &rVisualItem.mpScriptItem->a, - mpOutGlyphs + rVisualItem.mnMinGlyphPos, - mpLogClusters + rVisualItem.mnMinCharPos, - mpVisualAttrs + rVisualItem.mnMinGlyphPos, - &nGlyphCount ); - - if( nRC != 0 ) - continue; - - } - 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 ) - { - // #i55716# skip WORDJOINER - if( pStr[ 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 ) - { - // #i55716# skip WORDJOINER - if( pStr[ c ] == 0x2060 ) - mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1; - else - rArgs.NeedFallback( c, true ); - } - } - } - } while( ++i < nGlyphCount ); - } - } - - // now place the glyphs - nRC = ScriptPlace( 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 = ScriptGetLogicalWidths( - &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 - auto it = aDropChars.cbegin(); - while( it != aDropChars.cend() ) - { - // 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, DeviceCoordinate* pGlyphAdvances, int* pCharPosAry, - const PhysicalFontFace** /*pFallbackFonts*/ ) 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 ); - SAL_WARN_IF( !bRC, "vcl", "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; - - //Resolves: fdo#33090 Ensure that all glyph slots, even if 0-width - //or empty due to combining chars etc, map back to a character - //position so that iterating over glyph slots one at a time for - //glyph fallback can keep context as to what characters are the - //inputs that caused a missing glyph in a given font. - - //See: fdo#46923/fdo#46896/fdo#46750 for extra complexities - { - int dir = 1; - int out = rVI.mnMinCharPos; - if (rVI.IsRTL()) - { - dir = -1; - out = rVI.mnEndCharPos-1; - } - for(c = rVI.mnMinCharPos; c < rVI.mnEndCharPos; ++c) - { - int i = out - mnSubStringMin; - mpGlyphs2Chars[i] = c; - out += dir; - } - } - - // 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; - } - // use a heuristic to fill the gaps in the glyphs2chars array - c = !rVI.IsRTL() ? rVI.mnMinCharPos : rVI.mnEndCharPos - 1; - for( int i = rVI.mnMinGlyphPos; i < rVI.mnEndGlyphPos; ++i ) { - if( mpGlyphs2Chars[i] == -1 ) - mpGlyphs2Chars[i] = c; - else - c = mpGlyphs2Chars[i]; - } - } - } - - // 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 - if( (mnLayoutFlags & SalLayoutFlags::Vertical) && - nCharPos != -1 ) - aGlyphId |= GetVerticalFlags( msTheString[nCharPos] ); - *(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 ) -{ - SAL_WARN_IF( (nStartx8 & 0xff), "vcl", "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; - SAL_WARN_IF( nStart > mnGlyphCount, "vcl", "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 - SAL_WARN_IF( !bRC, "vcl", "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; - } - } ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits