diff options
Diffstat (limited to 'vcl/win/source/gdi')
-rw-r--r-- | vcl/win/source/gdi/MAKEFILE.MK | 4 | ||||
-rw-r--r-- | vcl/win/source/gdi/salgdi3.cxx | 127 | ||||
-rw-r--r-- | vcl/win/source/gdi/salgdi_gdiplus.cxx | 162 | ||||
-rwxr-xr-x | vcl/win/source/gdi/winlayout.cxx | 307 |
4 files changed, 470 insertions, 130 deletions
diff --git a/vcl/win/source/gdi/MAKEFILE.MK b/vcl/win/source/gdi/MAKEFILE.MK index a6d84d41f3ea..3d8fd904b35b 100644 --- a/vcl/win/source/gdi/MAKEFILE.MK +++ b/vcl/win/source/gdi/MAKEFILE.MK @@ -64,6 +64,10 @@ SLOFILES= $(SLO)$/salgdi.obj \ EXCEPTIONSFILES= $(SLO)$/salprn.obj +.IF "$(ENABLE_GRAPHITE)" == "TRUE" +CFLAGS+=-DENABLE_GRAPHITE +.ENDIF + # --- Targets ------------------------------------------------------ .INCLUDE : target.mk diff --git a/vcl/win/source/gdi/salgdi3.cxx b/vcl/win/source/gdi/salgdi3.cxx index 99f276faa964..73f4d8320acc 100644 --- a/vcl/win/source/gdi/salgdi3.cxx +++ b/vcl/win/source/gdi/salgdi3.cxx @@ -6,9 +6,6 @@ * * OpenOffice.org - a multi-platform office productivity suite * - * $RCSfile: salgdi3.cxx,v $ - * $Revision: 1.95.14.5 $ - * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify @@ -47,6 +44,7 @@ #include "vcl/svapp.hxx" #include "vcl/outfont.hxx" #include "vcl/font.hxx" +#include "vcl/fontsubset.hxx" #include "vcl/sallayout.hxx" #include "rtl/logfile.hxx" @@ -74,6 +72,11 @@ #include <algorithm> #endif +#ifdef ENABLE_GRAPHITE +#include <graphite/GrClient.h> +#include <graphite/WinFont.h> +#endif + #include <vector> #include <set> #include <map> @@ -551,13 +554,10 @@ static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXA& rE aDFA.mbEmbeddable = false; aDFA.mbSubsettable = false; - if( (rMetric.tmPitchAndFamily & TMPF_TRUETYPE) != 0 ) - if( (rMetric.tmPitchAndFamily & TMPF_DEVICE) == 0 ) - // TODO: implement type1 or CFF subsetting - if( 0 == (rMetric.ntmFlags & (NTM_PS_OPENTYPE | NTM_TYPE1) ) ) - aDFA.mbSubsettable = true; - // for now we can only embed Type1 fonts - if( 0 != (rMetric.ntmFlags & NTM_TYPE1 ) ) + if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) + || 0 != (rMetric.tmPitchAndFamily & TMPF_TRUETYPE)) + aDFA.mbSubsettable = true; + else if( 0 != (rMetric.tmPitchAndFamily & NTM_TYPE1) ) // TODO: implement subsetting for type1 too aDFA.mbEmbeddable = true; // heuristics for font quality @@ -566,7 +566,7 @@ static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXA& rE aDFA.mnQuality = 0; if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE ) aDFA.mnQuality += 50; - if( rMetric.ntmFlags & NTM_TT_OPENTYPE ) + if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) ) aDFA.mnQuality += 10; if( aDFA.mbSubsettable ) aDFA.mnQuality += 200; @@ -633,13 +633,10 @@ static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rE aDFA.mbEmbeddable = false; aDFA.mbSubsettable = false; - if( (rMetric.tmPitchAndFamily & TMPF_TRUETYPE) != 0 ) - if( (rMetric.tmPitchAndFamily & TMPF_DEVICE) == 0 ) - // TODO: implement type1 or CFF subsetting - if( 0 == (rMetric.ntmFlags & (NTM_PS_OPENTYPE | NTM_TYPE1) ) ) - aDFA.mbSubsettable = true; - // for now we can only embed Type1 fonts - if( rMetric.ntmFlags & NTM_TYPE1 ) + if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) + || 0 != (rMetric.tmPitchAndFamily & TMPF_TRUETYPE)) + aDFA.mbSubsettable = true; + else if( 0 != (rMetric.tmPitchAndFamily & NTM_TYPE1) ) // TODO: implement subsetting for type1 too aDFA.mbEmbeddable = true; // heuristics for font quality @@ -648,7 +645,7 @@ static ImplDevFontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rE aDFA.mnQuality = 0; if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE ) aDFA.mnQuality += 50; - if( rMetric.ntmFlags & NTM_TT_OPENTYPE ) + if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) ) aDFA.mnQuality += 10; if( aDFA.mbSubsettable ) aDFA.mnQuality += 200; @@ -807,6 +804,9 @@ ImplWinFontData::ImplWinFontData( const ImplDevFontAttributes& rDFS, mbDisableGlyphApi( false ), mbHasKoreanRange( false ), mbHasCJKSupport( false ), +#ifdef ENABLE_GRAPHITE + mbHasGraphiteSupport( false ), +#endif mbHasArabicSupport ( false ), mbAliasSymbolsLow( false ), mbAliasSymbolsHigh( false ), @@ -865,6 +865,13 @@ void ImplWinFontData::UpdateFromHDC( HDC hDC ) const ReadCmapTable( hDC ); ReadOs2Table( hDC ); +#ifdef ENABLE_GRAPHITE + static const char* pDisableGraphiteText = getenv( "SAL_DISABLE_GRAPHITE" ); + if( !pDisableGraphiteText || (pDisableGraphiteText[0] == '0') ) + { + mbHasGraphiteSupport = gr::WinFont::FontHasGraphiteTables(hDC); + } +#endif // even if the font works some fonts have problems with the glyph API // => the heuristic below tries to figure out which fonts have the problem @@ -993,27 +1000,25 @@ void ImplWinFontData::ReadGsubTable( HDC hDC ) const void ImplWinFontData::ReadCmapTable( HDC hDC ) const { - CmapResult aResult; - aResult.mnPairCount = 0; - aResult.mpPairCodes = NULL; - aResult.mpStartGlyphs = NULL; - aResult.mbSymbolic = (meWinCharSet == SYMBOL_CHARSET); - aResult.mbRecoded = true; + if( mpUnicodeMap != NULL ) + return; + bool bIsSymbolFont = (meWinCharSet == SYMBOL_CHARSET); // get the CMAP table from the font which is selected into the DC const DWORD nCmapTag = CalcTag( "cmap" ); const RawFontData aRawFontData( hDC, nCmapTag ); // parse the CMAP table if available - if( aRawFontData.get() ) + if( aRawFontData.get() ) { + CmapResult aResult; ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult ); + mbDisableGlyphApi |= aResult.mbRecoded; + aResult.mbSymbolic = bIsSymbolFont; + if( aResult.mnRangeCount > 0 ) + mpUnicodeMap = new ImplFontCharMap( aResult ); + } - mbDisableGlyphApi |= aResult.mbRecoded; - - if( aResult.mnPairCount > 0 ) - mpUnicodeMap = new ImplFontCharMap( aResult.mnPairCount, - aResult.mpPairCodes, aResult.mpStartGlyphs ); - else - mpUnicodeMap = ImplFontCharMap::GetDefaultMap(); + if( !mpUnicodeMap ) + mpUnicodeMap = ImplFontCharMap::GetDefaultMap( bIsSymbolFont ); } // ======================================================================= @@ -2547,6 +2552,8 @@ BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, const ImplFontData* pFont, long* pGlyphIDs, sal_uInt8* pEncoding, sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo ) { + // TODO: use more of the central font-subsetting code, move stuff there if needed + // create matching ImplFontSelectData // we need just enough to get to the font file data // use height=1000 for easier debugging (to match psprint's font units) @@ -2554,10 +2561,14 @@ BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, // TODO: much better solution: move SetFont and restoration of old font to caller ScopedFont aOldFont(*this); - float fScale = 0.0; + float fScale = 1.0; HFONT hOldFont = 0; ImplDoSetFont( &aIFSD, fScale, hOldFont ); + ImplWinFontData* pWinFontData = (ImplWinFontData*)aIFSD.mpFontData; + pWinFontData->UpdateFromHDC( mhDC ); +/*const*/ ImplFontCharMap* pImplFontCharMap = pWinFontData->GetImplFontCharMap(); + #if OSL_DEBUG_LEVEL > 1 // get font metrics TEXTMETRICA aWinMetric; @@ -2568,8 +2579,42 @@ BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" ); #endif + rtl::OUString aSysPath; + if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) + return FALSE; + const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); + const ByteString aToFile( aSysPath.getStr(), (xub_StrLen)aSysPath.getLength(), aThreadEncoding ); + + // check if the font has a CFF-table + const DWORD nCffTag = CalcTag( "CFF " ); + const RawFontData aRawCffData( mhDC, nCffTag ); + if( aRawCffData.get() ) + { + long nRealGlyphIds[ 256 ]; + for( int i = 0; i < nGlyphCount; ++i ) + { + // TODO: remap notdef glyph if needed + // TODO: use GDI's GetGlyphIndices instead? Does it handle GSUB properly? + sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; + if( pGlyphIDs[i] & GF_ISCHAR ) // remaining pseudo-glyphs need to be translated + nGlyphIdx = pImplFontCharMap->GetGlyphIndex( nGlyphIdx ); + if( (pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0) // TODO: vertical substitution + {/*####*/} + + nRealGlyphIds[i] = nGlyphIdx; + } + + // provide a font subset from the CFF-table + FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); + rInfo.LoadFont( FontSubsetInfo::CFF_FONT, aRawCffData.get(), aRawCffData.size() ); + bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL, + nRealGlyphIds, pEncoding, nGlyphCount, pGlyphWidths ); + fclose( pOutFile ); + return bRC; + } + // get raw font file data - const RawFontData xRawFontData( mhDC ); + const RawFontData xRawFontData( mhDC, NULL ); if( !xRawFontData.get() ) return FALSE; @@ -2585,7 +2630,7 @@ BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, TTGlobalFontInfo aTTInfo; ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo ); - rInfo.m_nFontType = SAL_FONTSUBSETINFO_TYPE_TRUETYPE; + rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; rInfo.m_aPSName = ImplSalGetUniString( aTTInfo.psname ); rInfo.m_nAscent = +aTTInfo.winAscent; rInfo.m_nDescent = -aTTInfo.winDescent; @@ -2593,7 +2638,7 @@ BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, Point( aTTInfo.xMax, aTTInfo.yMax ) ); rInfo.m_nCapHeight = aTTInfo.yMax; // Well ... - // subset glyphs and get their properties + // subset TTF-glyphs and get their properties // take care that subset fonts require the NotDef glyph in pos 0 int nOrigCount = nGlyphCount; USHORT aShortIDs[ 256 ]; @@ -2649,11 +2694,6 @@ BOOL WinSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, free( pMetrics ); // write subset into destination file - rtl::OUString aSysPath; - if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) - return FALSE; - rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); - ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) ); nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs, aTempEncs, nGlyphCount, 0, NULL, 0 ); return (nRC == SF_OK); @@ -2683,7 +2723,7 @@ const void* WinSalGraphics::GetEmbedFontData( const ImplFontData* pFont, TEXTMETRICA aTm; if( !::GetTextMetricsA( mhDC, &aTm ) ) *pDataLen = 0; - rInfo.m_nFontType = SAL_FONTSUBSETINFO_TYPE_TYPE1; + rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1; WCHAR aFaceName[64]; int nFNLen = ::GetTextFaceW( mhDC, 64, aFaceName ); // #i59854# strip eventual null byte @@ -2876,3 +2916,4 @@ SystemFontData WinSalGraphics::GetSysFontData( int nFallbacklevel ) const } //-------------------------------------------------------------------------- + diff --git a/vcl/win/source/gdi/salgdi_gdiplus.cxx b/vcl/win/source/gdi/salgdi_gdiplus.cxx index 8709fc872540..5c00c786e22d 100644 --- a/vcl/win/source/gdi/salgdi_gdiplus.cxx +++ b/vcl/win/source/gdi/salgdi_gdiplus.cxx @@ -64,75 +64,79 @@ void impAddB2DPolygonToGDIPlusGraphicsPath(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon) { - const sal_uInt32 nCount(rPolygon.count());
-
- if(nCount)
- {
- const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
- const bool bControls(rPolygon.areControlPointsUsed());
- basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
- Gdiplus::PointF aFCurr(Gdiplus::REAL(aCurr.getX()), Gdiplus::REAL(aCurr.getY()));
-
- for(sal_uInt32 a(0); a < nEdgeCount; a++)
- {
- const sal_uInt32 nNextIndex((a + 1) % nCount);
- const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
- const Gdiplus::PointF aFNext(Gdiplus::REAL(aNext.getX()), Gdiplus::REAL(aNext.getY()));
-
- if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
- {
- const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
- const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
-
- rPath.AddBezier(
- aFCurr,
- Gdiplus::PointF(Gdiplus::REAL(aCa.getX()), Gdiplus::REAL(aCa.getY())),
- Gdiplus::PointF(Gdiplus::REAL(aCb.getX()), Gdiplus::REAL(aCb.getY())),
- aFNext);
- }
- else
- {
- rPath.AddLine(aFCurr, aFNext);
- }
-
- if(a + 1 < nEdgeCount)
- {
- aCurr = aNext;
- aFCurr = aFNext;
- }
- }
- }
+ const sal_uInt32 nCount(rPolygon.count()); + + if(nCount) + { + const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1); + const bool bControls(rPolygon.areControlPointsUsed()); + basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0)); + Gdiplus::PointF aFCurr(Gdiplus::REAL(aCurr.getX()), Gdiplus::REAL(aCurr.getY())); + + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + const sal_uInt32 nNextIndex((a + 1) % nCount); + const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex)); + const Gdiplus::PointF aFNext(Gdiplus::REAL(aNext.getX()), Gdiplus::REAL(aNext.getY())); + + if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex))) + { + const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a)); + const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex)); + + rPath.AddBezier( + aFCurr, + Gdiplus::PointF(Gdiplus::REAL(aCa.getX()), Gdiplus::REAL(aCa.getY())), + Gdiplus::PointF(Gdiplus::REAL(aCb.getX()), Gdiplus::REAL(aCb.getY())), + aFNext); + } + else + { + rPath.AddLine(aFCurr, aFNext); + } + + if(a + 1 < nEdgeCount) + { + aCurr = aNext; + aFCurr = aFNext; + } + } + } } bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency) { - const sal_uInt32 nCount(rPolyPolygon.count());
+ const sal_uInt32 nCount(rPolyPolygon.count()); if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0)) { - Gdiplus::Graphics aGraphics(mhDC);
+ Gdiplus::Graphics aGraphics(mhDC); const sal_uInt8 aTrans((sal_uInt8)255 - (sal_uInt8)basegfx::fround(fTransparency * 255.0)); - Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor));
- Gdiplus::SolidBrush aTestBrush(aTestColor);
- Gdiplus::GraphicsPath aPath;
+ Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor)); + Gdiplus::SolidBrush aTestBrush(aTestColor); + Gdiplus::GraphicsPath aPath; for(sal_uInt32 a(0); a < nCount; a++) { - aPath.StartFigure(); + if(0 != a) + { + aPath.StartFigure(); // #i101491# not needed for first run + } + impAddB2DPolygonToGDIPlusGraphicsPath(aPath, rPolyPolygon.getB2DPolygon(a)); aPath.CloseFigure(); } if(getAntiAliasB2DDraw()) { - aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
- }
- else
- {
- aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
- }
-
- aGraphics.FillPath(&aTestBrush, &aPath);
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); + } + else + { + aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone); + } + + aGraphics.FillPath(&aTestBrush, &aPath); } return true; @@ -140,53 +144,59 @@ bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly bool WinSalGraphics::drawPolyLine(const basegfx::B2DPolygon& rPolygon, const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin eLineJoin) { - const sal_uInt32 nCount(rPolygon.count());
+ const sal_uInt32 nCount(rPolygon.count()); if(mbPen && nCount) { - Gdiplus::Graphics aGraphics(mhDC);
- Gdiplus::Color aTestColor(255, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
- Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX()));
- Gdiplus::GraphicsPath aPath;
-
- switch(eLineJoin)
- {
+ Gdiplus::Graphics aGraphics(mhDC); + Gdiplus::Color aTestColor(255, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor)); + Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX())); + Gdiplus::GraphicsPath aPath; + + switch(eLineJoin) + { default : // basegfx::B2DLINEJOIN_NONE : { break; } case basegfx::B2DLINEJOIN_BEVEL : { - aTestPen.SetLineJoin(Gdiplus::LineJoinBevel);
+ aTestPen.SetLineJoin(Gdiplus::LineJoinBevel); break; } case basegfx::B2DLINEJOIN_MIDDLE : case basegfx::B2DLINEJOIN_MITER : { const Gdiplus::REAL aMiterLimit(15.0); - aTestPen.SetMiterLimit(aMiterLimit);
- aTestPen.SetLineJoin(Gdiplus::LineJoinMiter);
+ aTestPen.SetMiterLimit(aMiterLimit); + aTestPen.SetLineJoin(Gdiplus::LineJoinMiter); break; } case basegfx::B2DLINEJOIN_ROUND : { - aTestPen.SetLineJoin(Gdiplus::LineJoinRound);
+ aTestPen.SetLineJoin(Gdiplus::LineJoinRound); break; } - }
+ } impAddB2DPolygonToGDIPlusGraphicsPath(aPath, rPolygon); -
- if(getAntiAliasB2DDraw())
- {
- aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
- }
- else
- {
- aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
- }
-
- aGraphics.DrawPath(&aTestPen, &aPath);
+ + if(rPolygon.isClosed()) + { + // #i101491# needed to create the correct line joins + aPath.CloseFigure(); + } + + if(getAntiAliasB2DDraw()) + { + aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); + } + else + { + aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone); + } + + aGraphics.DrawPath(&aTestPen, &aPath); } return true; diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx index 8d9347e7e352..2d335808e4c1 100755 --- a/vcl/win/source/gdi/winlayout.cxx +++ b/vcl/win/source/gdi/winlayout.cxx @@ -71,6 +71,17 @@ 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; @@ -1757,6 +1768,8 @@ bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem, 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 @@ -2041,11 +2054,25 @@ void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos ) long nDelta = nNewXPos - pVI->mnXOffset; if( nStart > nMinGlyphPos ) { - // move the glyph by expanding its left glyph - int i; + // move the glyph by expanding its left glyph but ignore dropped glyphs + int i, nLastUndropped = nMinGlyphPos - 1; for( i = nMinGlyphPos; i < nStart; ++i ) - nDelta -= mpGlyphAdvances[ i ]; - mpGlyphAdvances[ i-1 ] += nDelta; + { + 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 { @@ -2066,11 +2093,18 @@ void UniscribeLayout::DropGlyph( int nStartx8 ) --nStart; else // nStart<=0 for first visible glyph { - const VisualItem* pVI = mpVisualItems; + 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; @@ -2127,11 +2161,12 @@ void UniscribeLayout::Simplify( bool /*bIsBase*/ ) } // handle dropped glyphs at start of visual item - int nEndGlyphPos; - GetItemSubrange( rVI, i, nEndGlyphPos ); + int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos; + GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ); + i = nMinGlyphPos; while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) ) { - rVI.mnXOffset += pGlyphWidths[ i ]; + //rVI.mnXOffset += pGlyphWidths[ i ]; rVI.mnMinGlyphPos = ++i; } @@ -2141,6 +2176,17 @@ void UniscribeLayout::Simplify( bool /*bIsBase*/ ) 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 ) @@ -2157,9 +2203,10 @@ void UniscribeLayout::Simplify( bool /*bIsBase*/ ) mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ]; if( mpJustifications ) mpJustifications[ j ] = mpJustifications[ i ]; - int k = mpGlyphs2Chars[ i ]; + const int k = mpGlyphs2Chars[ i ]; mpGlyphs2Chars[ j ] = k; - mpLogClusters[ k ] = sal::static_int_cast<WORD>(j++); + const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos; + mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos); } rVI.mnEndGlyphPos = j; @@ -2751,6 +2798,234 @@ bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const #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 ) @@ -2766,6 +3041,11 @@ SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLe 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 @@ -2788,7 +3068,12 @@ SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLe BYTE eCharSet = ANSI_CHARSET; if( mpLogFont ) eCharSet = mpLogFont->lfCharSet; - pWinLayout = new SimpleWinLayout( mhDC, eCharSet, rFontFace, rFontInstance ); +#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 ) |