summaryrefslogtreecommitdiff
path: root/cppcanvas/source/mtfrenderer/implrenderer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'cppcanvas/source/mtfrenderer/implrenderer.cxx')
-rw-r--r--cppcanvas/source/mtfrenderer/implrenderer.cxx3168
1 files changed, 3168 insertions, 0 deletions
diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx
new file mode 100644
index 000000000000..e8a6468611f7
--- /dev/null
+++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx
@@ -0,0 +1,3168 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_cppcanvas.hxx"
+
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <canvas/verbosetrace.hxx>
+#include <osl/mutex.hxx>
+#include <vos/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include <rtl/logfile.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/anytostring.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppcanvas/canvas.hxx>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/geometry/RealPoint2D.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <canvas/canvastools.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/salbtype.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/graphictools.hxx>
+#include <tools/poly.hxx>
+#include <i18npool/mslangid.hxx>
+#include <implrenderer.hxx>
+#include <tools.hxx>
+#include <outdevstate.hxx>
+#include <action.hxx>
+#include <bitmapaction.hxx>
+#include <lineaction.hxx>
+#include <pointaction.hxx>
+#include <polypolyaction.hxx>
+#include <textaction.hxx>
+#include <transparencygroupaction.hxx>
+#include <vector>
+#include <algorithm>
+#include <iterator>
+#include <boost/scoped_array.hpp>
+#include "mtftools.hxx"
+#include "outdevstate.hxx"
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+using namespace ::com::sun::star;
+
+
+// free support functions
+// ======================
+namespace
+{
+ template < class MetaActionType > void setStateColor( MetaActionType* pAct,
+ bool& rIsColorSet,
+ uno::Sequence< double >& rColorSequence,
+ const cppcanvas::CanvasSharedPtr& rCanvas )
+ {
+ // set rIsColorSet and check for true at the same time
+ if( (rIsColorSet=pAct->IsSetting()) != false )
+ {
+ ::Color aColor( pAct->GetColor() );
+
+ // force alpha part of color to
+ // opaque. transparent painting is done
+ // explicitely via META_TRANSPARENT_ACTION
+ aColor.SetTransparency(0);
+ //aColor.SetTransparency(128);
+
+ rColorSequence = ::vcl::unotools::colorToDoubleSequence(
+ aColor,
+ rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
+ }
+ }
+
+
+ // state stack manipulators
+ // ------------------------
+ void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
+ {
+ rStates.clear();
+ const ::cppcanvas::internal::OutDevState aDefaultState;
+ rStates.push_back( aDefaultState );
+ }
+
+ ::cppcanvas::internal::OutDevState& getState( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
+ {
+ return rStates.back();
+ }
+
+ const ::cppcanvas::internal::OutDevState& getState( const ::cppcanvas::internal::VectorOfOutDevStates& rStates )
+ {
+ return rStates.back();
+ }
+
+ void pushState( ::cppcanvas::internal::VectorOfOutDevStates& rStates,
+ sal_uInt16 nFlags )
+ {
+ rStates.push_back( getState( rStates ) );
+ getState( rStates ).pushFlags = nFlags;
+ }
+
+ void popState( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
+ {
+ if( getState( rStates ).pushFlags != PUSH_ALL )
+ {
+ // a state is pushed which is incomplete, i.e. does not
+ // restore everything to the previous stack level when
+ // popped.
+ // That means, we take the old state, and restore every
+ // OutDevState member whose flag is set, from the new to the
+ // old state. Then the new state gets overwritten by the
+ // calculated state
+
+ // preset to-be-calculated new state with old state
+ ::cppcanvas::internal::OutDevState aCalculatedNewState( getState( rStates ) );
+
+ // selectively copy to-be-restored content over saved old
+ // state
+ rStates.pop_back();
+
+ const ::cppcanvas::internal::OutDevState& rNewState( getState( rStates ) );
+
+ if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
+ {
+ aCalculatedNewState.lineColor = rNewState.lineColor;
+ aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
+ {
+ aCalculatedNewState.fillColor = rNewState.fillColor;
+ aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
+ {
+ aCalculatedNewState.xFont = rNewState.xFont;
+ aCalculatedNewState.fontRotation = rNewState.fontRotation;
+ aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle;
+ aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle;
+ aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle;
+ aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle;
+ aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle;
+ aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet;
+ aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet;
+ aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
+ {
+ aCalculatedNewState.textColor = rNewState.textColor;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
+ {
+ aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
+ {
+ aCalculatedNewState.clip = rNewState.clip;
+ aCalculatedNewState.clipRect = rNewState.clipRect;
+ aCalculatedNewState.xClipPoly = rNewState.xClipPoly;
+ }
+
+ // TODO(F2): Raster ops NYI
+ // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
+ // {
+ // }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
+ {
+ aCalculatedNewState.textFillColor = rNewState.textFillColor;
+ aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
+ {
+ aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
+ }
+
+ // TODO(F1): Refpoint handling NYI
+ // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
+ // {
+ // }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
+ {
+ aCalculatedNewState.textLineColor = rNewState.textLineColor;
+ aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
+ }
+
+ if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
+ {
+ aCalculatedNewState.textAlignment = rNewState.textAlignment;
+ aCalculatedNewState.textDirection = rNewState.textDirection;
+ }
+
+ // TODO(F2): Text language handling NYI
+ // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
+ // {
+ // }
+
+ // always copy push mode
+ aCalculatedNewState.pushFlags = rNewState.pushFlags;
+
+ // flush to stack
+ getState( rStates ) = aCalculatedNewState;
+ }
+ else
+ {
+ rStates.pop_back();
+ }
+ }
+
+ void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes,
+ const ::cppcanvas::internal::ActionFactoryParameters& rParms,
+ const LineInfo& rLineInfo )
+ {
+ const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
+ o_rStrokeAttributes.StrokeWidth =
+ (getState( rParms.mrStates ).mapModeTransform * aWidth).getX();
+
+ // setup reasonable defaults
+ o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
+ o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
+ o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
+
+ switch(rLineInfo.GetLineJoin())
+ {
+ default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
+ o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
+ break;
+ case basegfx::B2DLINEJOIN_BEVEL:
+ o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
+ break;
+ case basegfx::B2DLINEJOIN_MITER:
+ o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
+ break;
+ case basegfx::B2DLINEJOIN_ROUND:
+ o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
+ break;
+ }
+
+ if( LINE_DASH == rLineInfo.GetStyle() )
+ {
+ const ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
+
+ // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
+
+ // interpret dash info only if explicitely enabled as
+ // style
+ const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
+ const double nDistance( (rState.mapModeTransform * aDistance).getX() );
+
+ const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
+ const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
+
+ const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
+ const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
+
+ const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
+ 2*rLineInfo.GetDotCount() );
+
+ o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
+ double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
+
+
+ // iteratively fill dash array, first with dashs, then
+ // with dots.
+ // ===================================================
+
+ sal_Int32 nCurrEntry=0;
+
+ for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
+ {
+ pDashArray[nCurrEntry++] = nDashLen;
+ pDashArray[nCurrEntry++] = nDistance;
+ }
+ for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
+ {
+ pDashArray[nCurrEntry++] = nDotLen;
+ pDashArray[nCurrEntry++] = nDistance;
+ }
+ }
+ }
+
+
+ /** Create masked BitmapEx, where the white areas of rBitmap are
+ transparent, and the other appear in rMaskColor.
+ */
+ BitmapEx createMaskBmpEx( const Bitmap& rBitmap,
+ const ::Color& rMaskColor )
+ {
+ const ::Color aWhite( COL_WHITE );
+ BitmapPalette aBiLevelPalette(2);
+ aBiLevelPalette[0] = aWhite;
+ aBiLevelPalette[1] = rMaskColor;
+
+ Bitmap aMask( rBitmap.CreateMask( aWhite ));
+ Bitmap aSolid( rBitmap.GetSizePixel(),
+ 1,
+ &aBiLevelPalette );
+ aSolid.Erase( rMaskColor );
+
+ return BitmapEx( aSolid, aMask );
+ }
+
+ /** Shameless rip from vcl/source/gdi/outdev3.cxx
+
+ Should consolidate, into something like basetxt...
+ */
+ sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang )
+ {
+ // currently only conversion from ASCII digits is interesting
+ if( (nChar < '0') || ('9' < nChar) )
+ return nChar;
+
+ sal_Unicode nOffset(0);
+ // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
+ // CAVEAT! To some like Mongolian MS assigned the same primary language
+ // although the script type is different!
+ switch( eLang & LANGUAGE_MASK_PRIMARY )
+ {
+ default:
+ break;
+
+ case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
+ case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
+ case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
+ nOffset = 0x0660 - '0'; // arabic/persian/urdu
+ break;
+ case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x09E6 - '0'; // bengali
+ break;
+ case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x1040 - '0'; // burmese
+ break;
+ case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0966 - '0'; // devanagari
+ break;
+ case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0AE6 - '0'; // gujarati
+ break;
+ case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0CE6 - '0'; // kannada
+ break;
+ case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x17E0 - '0'; // khmer
+ break;
+ case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0ED0 - '0'; // lao
+ break;
+ case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0D66 - '0'; // malayalam
+ break;
+ case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY:
+ if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
+ nOffset = 0x1810 - '0'; // mongolian
+ else
+ nOffset = 0; // mongolian cyrillic
+ break;
+ case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0B66 - '0'; // oriya
+ break;
+ case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0BE7 - '0'; // tamil
+ break;
+ case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0C66 - '0'; // telugu
+ break;
+ case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0E50 - '0'; // thai
+ break;
+ case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
+ nOffset = 0x0F20 - '0'; // tibetan
+ break;
+ }
+
+ nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset);
+ return nChar;
+ }
+
+ void convertToLocalizedNumerals( XubString& rStr,
+ LanguageType eTextLanguage )
+ {
+ const sal_Unicode* pBase = rStr.GetBuffer();
+ const sal_Unicode* pBegin = pBase + 0;
+ const xub_StrLen nEndIndex = rStr.Len();
+ const sal_Unicode* pEnd = pBase + nEndIndex;
+
+ for( ; pBegin < pEnd; ++pBegin )
+ {
+ // TODO: are there non-digit localizations?
+ if( (*pBegin >= '0') && (*pBegin <= '9') )
+ {
+ // translate characters to local preference
+ sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage );
+ if( cChar != *pBegin )
+ rStr.SetChar( sal::static_int_cast<sal_uInt16>(pBegin - pBase), cChar );
+ }
+ }
+ }
+}
+
+
+namespace cppcanvas
+{
+ namespace internal
+ {
+ bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
+ const ActionFactoryParameters& rParms )
+ {
+ const OutDevState& rState( getState( rParms.mrStates ) );
+ if( (!rState.isLineColorSet &&
+ !rState.isFillColorSet) ||
+ (rState.lineColor.getLength() == 0 &&
+ rState.fillColor.getLength() == 0) )
+ {
+ return false;
+ }
+
+ ActionSharedPtr pPolyAction(
+ internal::PolyPolyActionFactory::createPolyPolyAction(
+ rPolyPoly, rParms.mrCanvas, rState ) );
+
+ if( pPolyAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPolyAction,
+ rParms.mrCurrActionIndex ) );
+
+ rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
+ }
+
+ return true;
+ }
+
+ bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly,
+ const ActionFactoryParameters& rParms )
+ {
+ return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
+ rParms );
+ }
+
+ void ImplRenderer::skipContent( GDIMetaFile& rMtf,
+ const char* pCommentString,
+ sal_Int32& io_rCurrActionIndex ) const
+ {
+ ENSURE_OR_THROW( pCommentString,
+ "ImplRenderer::skipContent(): NULL string given" );
+
+ MetaAction* pCurrAct;
+ while( (pCurrAct=rMtf.NextAction()) != NULL )
+ {
+ // increment action index, we've skipped an action.
+ ++io_rCurrActionIndex;
+
+ if( pCurrAct->GetType() == META_COMMENT_ACTION &&
+ static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii(
+ pCommentString ) == COMPARE_EQUAL )
+ {
+ // requested comment found, done
+ return;
+ }
+ }
+
+ // EOF
+ return;
+ }
+
+ bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
+ const char* pCommentString,
+ sal_uInt16 nType ) const
+ {
+ ENSURE_OR_THROW( pCommentString,
+ "ImplRenderer::isActionContained(): NULL string given" );
+
+ bool bRet( false );
+
+ // at least _one_ call to GDIMetaFile::NextAction() is
+ // executed
+ sal_uIntPtr nPos( 1 );
+
+ MetaAction* pCurrAct;
+ while( (pCurrAct=rMtf.NextAction()) != NULL )
+ {
+ if( pCurrAct->GetType() == nType )
+ {
+ bRet = true; // action type found
+ break;
+ }
+
+ if( pCurrAct->GetType() == META_COMMENT_ACTION &&
+ static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii(
+ pCommentString ) == COMPARE_EQUAL )
+ {
+ // delimiting end comment found, done
+ bRet = false; // not yet found
+ break;
+ }
+
+ ++nPos;
+ }
+
+ // rewind metafile to previous position (this method must
+ // not change the current metaaction)
+ while( nPos-- )
+ rMtf.WindPrev();
+
+ if( !pCurrAct )
+ {
+ // EOF, and not yet found
+ bRet = false;
+ }
+
+ return bRet;
+ }
+
+ void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly,
+ const ::Gradient& rGradient,
+ const ActionFactoryParameters& rParms,
+ bool bIsPolygonRectangle,
+ bool bSubsettableActions )
+ {
+ DBG_TESTSOLARMUTEX();
+
+ ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
+ aDevicePoly.transform( getState( rParms.mrStates ).mapModeTransform );
+
+ // decide, whether this gradient can be rendered natively
+ // by the canvas, or must be emulated via VCL gradient
+ // action extraction.
+ const sal_uInt16 nSteps( rGradient.GetSteps() );
+
+ if( // step count is infinite, can use native canvas
+ // gradients here
+ nSteps == 0 ||
+ // step count is sufficiently high, such that no
+ // discernible difference should be visible.
+ nSteps > 64 )
+ {
+ uno::Reference< lang::XMultiServiceFactory> xFactory(
+ rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
+
+ if( xFactory.is() )
+ {
+ rendering::Texture aTexture;
+
+ aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
+ aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
+ aTexture.Alpha = 1.0;
+
+
+ // setup start/end color values
+ // ----------------------------
+
+ // scale color coefficients with gradient intensities
+ const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
+ ::Color aVCLStartColor( rGradient.GetStartColor() );
+ aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
+ aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
+ aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
+
+ const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
+ ::Color aVCLEndColor( rGradient.GetEndColor() );
+ aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
+ aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
+ aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
+
+ uno::Reference<rendering::XColorSpace> xColorSpace(
+ rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
+ const uno::Sequence< double > aStartColor(
+ ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
+ xColorSpace ));
+ const uno::Sequence< double > aEndColor(
+ ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
+ xColorSpace ));
+
+ uno::Sequence< uno::Sequence < double > > aColors(2);
+ uno::Sequence< double > aStops(2);
+
+ if( rGradient.GetStyle() == GRADIENT_AXIAL )
+ {
+ aStops.realloc(3);
+ aColors.realloc(3);
+
+ aStops[0] = 0.0;
+ aStops[1] = 0.5;
+ aStops[2] = 1.0;
+
+ aColors[0] = aEndColor;
+ aColors[1] = aStartColor;
+ aColors[2] = aEndColor;
+ }
+ else
+ {
+ aStops[0] = 0.0;
+ aStops[1] = 1.0;
+
+ aColors[0] = aStartColor;
+ aColors[1] = aEndColor;
+ }
+
+ const ::basegfx::B2DRectangle aBounds(
+ ::basegfx::tools::getRange(aDevicePoly) );
+ const ::basegfx::B2DVector aOffset(
+ rGradient.GetOfsX() / 100.0,
+ rGradient.GetOfsY() / 100.0);
+ double fRotation( rGradient.GetAngle() * M_PI / 1800.0 );
+ const double fBorder( rGradient.GetBorder() / 100.0 );
+
+ basegfx::B2DHomMatrix aRot90;
+ aRot90.rotate(M_PI_2);
+
+ basegfx::ODFGradientInfo aGradInfo;
+ rtl::OUString aGradientService;
+ switch( rGradient.GetStyle() )
+ {
+ case GRADIENT_LINEAR:
+ basegfx::tools::createLinearODFGradientInfo(aGradInfo,
+ aBounds,
+ nSteps,
+ fBorder,
+ fRotation);
+ // map odf to svg gradient orientation - x
+ // instead of y direction
+ aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
+ aGradientService = rtl::OUString::createFromAscii("LinearGradient");
+ break;
+
+ case GRADIENT_AXIAL:
+ {
+ // Adapt the border so that it is suitable
+ // for the axial gradient. An axial
+ // gradient consists of two linear
+ // gradients. Each of those covers half
+ // of the total size. In order to
+ // compensate for the condensed display of
+ // the linear gradients, we have to
+ // enlarge the area taken up by the actual
+ // gradient (1-fBorder). After that we
+ // have to turn the result back into a
+ // border value, hence the second (left
+ // most 1-...
+ const double fAxialBorder (1-2*(1-fBorder));
+ basegfx::tools::createAxialODFGradientInfo(aGradInfo,
+ aBounds,
+ nSteps,
+ fAxialBorder,
+ fRotation);
+ // map odf to svg gradient orientation - x
+ // instead of y direction
+ aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
+
+ // map odf axial gradient to 3-stop linear
+ // gradient - shift left by 0.5
+ basegfx::B2DHomMatrix aShift;
+ aShift.translate(-0.5,0);
+ aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift;
+
+ aGradientService = rtl::OUString::createFromAscii("LinearGradient");
+ break;
+ }
+
+ case GRADIENT_RADIAL:
+ basegfx::tools::createRadialODFGradientInfo(aGradInfo,
+ aBounds,
+ aOffset,
+ nSteps,
+ fBorder);
+ aGradientService = rtl::OUString::createFromAscii("EllipticalGradient");
+ break;
+
+ case GRADIENT_ELLIPTICAL:
+ basegfx::tools::createEllipticalODFGradientInfo(aGradInfo,
+ aBounds,
+ aOffset,
+ nSteps,
+ fBorder,
+ fRotation);
+ aGradientService = rtl::OUString::createFromAscii("EllipticalGradient");
+ break;
+
+ case GRADIENT_SQUARE:
+ basegfx::tools::createSquareODFGradientInfo(aGradInfo,
+ aBounds,
+ aOffset,
+ nSteps,
+ fBorder,
+ fRotation);
+ aGradientService = rtl::OUString::createFromAscii("RectangularGradient");
+ break;
+
+ case GRADIENT_RECT:
+ basegfx::tools::createRectangularODFGradientInfo(aGradInfo,
+ aBounds,
+ aOffset,
+ nSteps,
+ fBorder,
+ fRotation);
+ aGradientService = rtl::OUString::createFromAscii("RectangularGradient");
+ break;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "ImplRenderer::createGradientAction(): Unexpected gradient type" );
+ break;
+ }
+
+ // As the texture coordinate space is relative to
+ // the polygon coordinate space (NOT to the
+ // polygon itself), move gradient to the start of
+ // the actual polygon. If we skip this, the
+ // gradient will always display at the origin, and
+ // not within the polygon bound (which might be
+ // miles away from the origin).
+ aGradInfo.maTextureTransform.translate( aBounds.getMinX(),
+ aBounds.getMinY() );
+ ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
+ aGradInfo.maTextureTransform );
+
+ uno::Sequence<uno::Any> args(3);
+ beans::PropertyValue aProp;
+ aProp.Name = rtl::OUString::createFromAscii("Colors");
+ aProp.Value <<= aColors;
+ args[0] <<= aProp;
+ aProp.Name = rtl::OUString::createFromAscii("Stops");
+ aProp.Value <<= aStops;
+ args[1] <<= aProp;
+ aProp.Name = rtl::OUString::createFromAscii("AspectRatio");
+ aProp.Value <<= aGradInfo.mfAspectRatio;
+ args[2] <<= aProp;
+
+ aTexture.Gradient.set(
+ xFactory->createInstanceWithArguments(aGradientService,
+ args),
+ uno::UNO_QUERY);
+ if( aTexture.Gradient.is() )
+ {
+ ActionSharedPtr pPolyAction(
+ internal::PolyPolyActionFactory::createPolyPolyAction(
+ aDevicePoly,
+ rParms.mrCanvas,
+ getState( rParms.mrStates ),
+ aTexture ) );
+
+ if( pPolyAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPolyAction,
+ rParms.mrCurrActionIndex ) );
+
+ rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
+ }
+
+ // done, using native gradients
+ return;
+ }
+ }
+ }
+
+ // cannot currently use native canvas gradients, as a
+ // finite step size is given (this funny feature is not
+ // supported by the XCanvas API)
+ pushState( rParms.mrStates, PUSH_ALL );
+
+ if( !bIsPolygonRectangle )
+ {
+ // only clip, if given polygon is not a rectangle in
+ // the first place (the gradient is always limited to
+ // the given bound rect)
+ updateClipping(
+ aDevicePoly,
+ rParms,
+ true );
+ }
+
+ GDIMetaFile aTmpMtf;
+ rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
+ rGradient,
+ aTmpMtf );
+
+ createActions( aTmpMtf, rParms, bSubsettableActions );
+
+ popState( rParms.mrStates );
+ }
+
+ uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation,
+ const ::Font& rFont,
+ const ActionFactoryParameters& rParms ) const
+ {
+ rendering::FontRequest aFontRequest;
+
+ if( rParms.mrParms.maFontName.is_initialized() )
+ aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
+ else
+ aFontRequest.FontDescription.FamilyName = rFont.GetName();
+
+ aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
+
+ aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
+ aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
+
+ // TODO(F2): improve vclenum->panose conversion
+ aFontRequest.FontDescription.FontDescription.Weight =
+ rParms.mrParms.maFontWeight.is_initialized() ?
+ *rParms.mrParms.maFontWeight :
+ ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
+ aFontRequest.FontDescription.FontDescription.Letterform =
+ rParms.mrParms.maFontLetterForm.is_initialized() ?
+ *rParms.mrParms.maFontLetterForm :
+ (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
+
+ LanguageType aLang = rFont.GetLanguage();
+ aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false);
+
+ // setup state-local text transformation,
+ // if the font be rotated
+ const short nFontAngle( rFont.GetOrientation() );
+ if( nFontAngle != 0 )
+ {
+ // set to unity transform rotated by font angle
+ const double nAngle( nFontAngle * (F_PI / 1800.0) );
+ o_rFontRotation = -nAngle;
+ }
+ else
+ {
+ o_rFontRotation = 0.0;
+ }
+
+ geometry::Matrix2D aFontMatrix;
+ ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
+
+ // TODO(F2): use correct scale direction, font
+ // height might be width or anything else
+
+ // TODO(Q3): This code smells of programming by
+ // coincidence (the next two if statements)
+ const ::Size rFontSizeLog( rFont.GetSize() );
+ const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
+ if( nFontWidthLog != 0 )
+ {
+ ::Font aTestFont = rFont;
+ aTestFont.SetWidth( 0 );
+ sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
+ if( nNormalWidth != nFontWidthLog )
+ if( nNormalWidth )
+ aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
+ }
+
+ // #i52608# apply map mode scale also to font matrix - an
+ // anisotrophic mapmode must be reflected in an
+ // anisotrophic font matrix scale.
+ const OutDevState& rState( getState( rParms.mrStates ) );
+ if( !::basegfx::fTools::equal(
+ rState.mapModeTransform.get(0,0),
+ rState.mapModeTransform.get(1,1)) )
+ {
+ const double nScaleX( rState.mapModeTransform.get(0,0) );
+ const double nScaleY( rState.mapModeTransform.get(1,1) );
+
+ // note: no reason to check for division by zero, we
+ // always have the value closer (or equal) to zero as
+ // the nominator.
+ if( fabs(nScaleX) < fabs(nScaleY) )
+ aFontMatrix.m00 *= nScaleX / nScaleY;
+ else
+ aFontMatrix.m11 *= nScaleY / nScaleX;
+ }
+ aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
+
+ return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
+ uno::Sequence< beans::PropertyValue >(),
+ aFontMatrix );
+ }
+
+ // create text effects such as shadow/relief/embossed
+ void ImplRenderer::createTextAction( const ::Point& rStartPoint,
+ const String rString,
+ int nIndex,
+ int nLength,
+ const sal_Int32* pCharWidths,
+ const ActionFactoryParameters& rParms,
+ bool bSubsettableActions )
+ {
+ ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex,
+ "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
+
+ if( !nLength )
+ return; // zero-length text, no visible output
+
+ const OutDevState& rState( getState( rParms.mrStates ) );
+
+ // TODO(F2): implement all text effects
+ // if( rState.textAlignment ); // TODO(F2): NYI
+
+ ::Color aShadowColor( COL_AUTO );
+ ::Color aReliefColor( COL_AUTO );
+ ::Size aShadowOffset;
+ ::Size aReliefOffset;
+
+ uno::Reference<rendering::XColorSpace> xColorSpace(
+ rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
+
+ if( rState.isTextEffectShadowSet )
+ {
+ // calculate shadow offset (similar to outdev3.cxx)
+ // TODO(F3): better match with outdev3.cxx
+ sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
+ if( nShadowOffset < 1 )
+ nShadowOffset = 1;
+
+ aShadowOffset.setWidth( nShadowOffset );
+ aShadowOffset.setHeight( nShadowOffset );
+
+ // determine shadow color (from outdev3.cxx)
+ ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
+ rState.textColor, xColorSpace );
+ bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
+ || (aTextColor.GetLuminance() < 8);
+
+ aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
+ aShadowColor.SetTransparency( aTextColor.GetTransparency() );
+ }
+
+ if( rState.textReliefStyle )
+ {
+ // calculate relief offset (similar to outdev3.cxx)
+ sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
+ nReliefOffset += nReliefOffset/2;
+ if( nReliefOffset < 1 )
+ nReliefOffset = 1;
+
+ if( rState.textReliefStyle == RELIEF_ENGRAVED )
+ nReliefOffset = -nReliefOffset;
+
+ aReliefOffset.setWidth( nReliefOffset );
+ aReliefOffset.setHeight( nReliefOffset );
+
+ // determine relief color (from outdev3.cxx)
+ ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
+ rState.textColor, xColorSpace );
+
+ aReliefColor = ::Color( COL_LIGHTGRAY );
+
+ // we don't have a automatic color, so black is always
+ // drawn on white (literally copied from
+ // vcl/source/gdi/outdev3.cxx)
+ if( aTextColor.GetColor() == COL_BLACK )
+ {
+ aTextColor = ::Color( COL_WHITE );
+ getState( rParms.mrStates ).textColor =
+ ::vcl::unotools::colorToDoubleSequence(
+ aTextColor, xColorSpace );
+ }
+
+ if( aTextColor.GetColor() == COL_WHITE )
+ aReliefColor = ::Color( COL_BLACK );
+ aReliefColor.SetTransparency( aTextColor.GetTransparency() );
+ }
+
+ // create the actual text action
+ ActionSharedPtr pTextAction(
+ TextActionFactory::createTextAction(
+ rStartPoint,
+ aReliefOffset,
+ aReliefColor,
+ aShadowOffset,
+ aShadowColor,
+ rString,
+ nIndex,
+ nLength,
+ pCharWidths,
+ rParms.mrVDev,
+ rParms.mrCanvas,
+ rState,
+ rParms.mrParms,
+ bSubsettableActions ) );
+
+ ActionSharedPtr pStrikeoutTextAction;
+
+ if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
+ {
+ long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
+
+ xub_Unicode pChars[5];
+ if ( rState.textStrikeoutStyle == STRIKEOUT_X )
+ pChars[0] = 'X';
+ else
+ pChars[0] = '/';
+ pChars[3]=pChars[2]=pChars[1]=pChars[0];
+
+ long nStrikeoutWidth = nWidth;
+ String aStrikeoutTest( pChars, 4 );
+
+ if( aStrikeoutTest.Len() )
+ {
+ nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4;
+ aStrikeoutTest.Erase();
+
+ if( nStrikeoutWidth <= 0 )
+ nStrikeoutWidth = 1;
+ }
+
+ long nMaxWidth = nStrikeoutWidth/2;
+ if ( nMaxWidth < 2 )
+ nMaxWidth = 2;
+ nMaxWidth += nWidth + 1;
+
+ long nFullStrikeoutWidth = 0;
+ String aStrikeoutText( pChars, 0 );
+ while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
+ aStrikeoutText += pChars[0];
+
+
+ sal_Int32 nStartPos = 0;
+ xub_StrLen nLen = aStrikeoutText.Len();
+
+ if( nLen )
+ {
+ long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
+ nStrikeoutWidth += nInterval;
+ sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
+
+ for ( int i = 0;i<nLen; i++)
+ {
+ pStrikeoutCharWidths[i] = nStrikeoutWidth;
+ }
+
+ for ( int i = 1;i< nLen; i++ )
+ {
+ pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
+ }
+
+ pStrikeoutTextAction =
+ TextActionFactory::createTextAction(
+ rStartPoint,
+ aReliefOffset,
+ aReliefColor,
+ aShadowOffset,
+ aShadowColor,
+ aStrikeoutText,
+ nStartPos,
+ aStrikeoutText.Len(),
+ pStrikeoutCharWidths,
+ rParms.mrVDev,
+ rParms.mrCanvas,
+ rState,
+ rParms.mrParms,
+ bSubsettableActions ) ;
+ }
+ }
+
+ if( pTextAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pTextAction,
+ rParms.mrCurrActionIndex ) );
+
+ if ( pStrikeoutTextAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pStrikeoutTextAction,
+ rParms.mrCurrActionIndex ) );
+ }
+
+ rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
+ }
+ }
+
+ void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
+ const ActionFactoryParameters& rParms,
+ bool bIntersect )
+ {
+ ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
+ ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
+
+ const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
+ const bool bEmptyClipPoly( rState.clip.count() == 0 );
+
+ ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
+ "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
+
+ if( !bIntersect ||
+ (bEmptyClipRect && bEmptyClipPoly) )
+ {
+ rState.clip = rClipPoly;
+ }
+ else
+ {
+ if( !bEmptyClipRect )
+ {
+ // TODO(P3): Use Liang-Barsky polygon clip here,
+ // after all, one object is just a rectangle!
+
+ // convert rect to polygon beforehand, must revert
+ // to general polygon clipping here.
+ rState.clip = ::basegfx::B2DPolyPolygon(
+ ::basegfx::tools::createPolygonFromRect(
+ // #121100# VCL rectangular clips always
+ // include one more pixel to the right
+ // and the bottom
+ ::basegfx::B2DRectangle( rState.clipRect.Left(),
+ rState.clipRect.Top(),
+ rState.clipRect.Right()+1,
+ rState.clipRect.Bottom()+1 ) ) );
+ }
+
+ // AW: Simplified
+ rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
+ aClipPoly, rState.clip, true, false);
+ }
+
+ // by now, our clip resides in the OutDevState::clip
+ // poly-polygon.
+ rState.clipRect.SetEmpty();
+
+ if( rState.clip.count() == 0 )
+ {
+ if( rState.clipRect.IsEmpty() )
+ {
+ rState.xClipPoly.clear();
+ }
+ else
+ {
+ rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rParms.mrCanvas->getUNOCanvas()->getDevice(),
+ ::basegfx::B2DPolyPolygon(
+ ::basegfx::tools::createPolygonFromRect(
+ // #121100# VCL rectangular clips
+ // always include one more pixel to
+ // the right and the bottom
+ ::basegfx::B2DRectangle( rState.clipRect.Left(),
+ rState.clipRect.Top(),
+ rState.clipRect.Right()+1,
+ rState.clipRect.Bottom()+1 ) ) ) );
+ }
+ }
+ else
+ {
+ rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rParms.mrCanvas->getUNOCanvas()->getDevice(),
+ rState.clip );
+ }
+ }
+
+ void ImplRenderer::updateClipping( const ::Rectangle& rClipRect,
+ const ActionFactoryParameters& rParms,
+ bool bIntersect )
+ {
+ ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
+
+ const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
+ const bool bEmptyClipPoly( rState.clip.count() == 0 );
+
+ ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
+ "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
+
+ if( !bIntersect ||
+ (bEmptyClipRect && bEmptyClipPoly) )
+ {
+ rState.clipRect = rClipRect;
+ rState.clip.clear();
+ }
+ else if( bEmptyClipPoly )
+ {
+ rState.clipRect.Intersection( rClipRect );
+ rState.clip.clear();
+ }
+ else
+ {
+ // TODO(P3): Handle a fourth case here, when all clip
+ // polygons are rectangular, once B2DMultiRange's
+ // sweep line implementation is done.
+
+ // general case: convert to polygon and clip
+ // -----------------------------------------
+
+ // convert rect to polygon beforehand, must revert
+ // to general polygon clipping here.
+ ::basegfx::B2DPolyPolygon aClipPoly(
+ ::basegfx::tools::createPolygonFromRect(
+ ::basegfx::B2DRectangle( rClipRect.Left(),
+ rClipRect.Top(),
+ rClipRect.Right(),
+ rClipRect.Bottom() ) ) );
+
+ rState.clipRect.SetEmpty();
+
+ // AW: Simplified
+ rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
+ aClipPoly, rState.clip, true, false);
+ }
+
+ if( rState.clip.count() == 0 )
+ {
+ if( rState.clipRect.IsEmpty() )
+ {
+ rState.xClipPoly.clear();
+ }
+ else
+ {
+ rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rParms.mrCanvas->getUNOCanvas()->getDevice(),
+ ::basegfx::B2DPolyPolygon(
+ ::basegfx::tools::createPolygonFromRect(
+ // #121100# VCL rectangular clips
+ // always include one more pixel to
+ // the right and the bottom
+ ::basegfx::B2DRectangle( rState.clipRect.Left(),
+ rState.clipRect.Top(),
+ rState.clipRect.Right()+1,
+ rState.clipRect.Bottom()+1 ) ) ) );
+ }
+ }
+ else
+ {
+ rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rParms.mrCanvas->getUNOCanvas()->getDevice(),
+ rState.clip );
+ }
+ }
+
+ bool ImplRenderer::createActions( GDIMetaFile& rMtf,
+ const ActionFactoryParameters& rFactoryParms,
+ bool bSubsettableActions )
+ {
+ /* TODO(P2): interpret mtf-comments
+ ================================
+
+ - gradient fillings (do that via comments)
+
+ - think about mapping. _If_ we do everything in logical
+ coordinates (which would solve the probs for stroke
+ widths and text offsets), then we would have to
+ recalc scaling for every drawing operation. This is
+ because the outdev map mode might change at any time.
+ Also keep in mind, that, although we've double precision
+ float arithmetic now, different offsets might still
+ generate different roundings (aka
+ 'OutputDevice::SetPixelOffset())
+
+ */
+
+ // alias common parameters
+ VectorOfOutDevStates& rStates(rFactoryParms.mrStates);
+ const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
+ ::VirtualDevice& rVDev(rFactoryParms.mrVDev);
+ const Parameters& rParms(rFactoryParms.mrParms);
+ sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
+
+
+ // Loop over every metaaction
+ // ==========================
+ MetaAction* pCurrAct;
+
+ // TODO(P1): think about caching
+ for( pCurrAct=rMtf.FirstAction();
+ pCurrAct;
+ pCurrAct = rMtf.NextAction() )
+ {
+ // execute every action, to keep VDev state up-to-date
+ // currently used only for
+ // - the map mode
+ // - the line/fill color when processing a META_TRANSPARENT_ACTION
+ // - SetFont to process font metric specific actions
+ pCurrAct->Execute( &rVDev );
+
+ switch( pCurrAct->GetType() )
+ {
+ // ------------------------------------------------------------
+
+ // In the first part of this monster-switch, we
+ // handle all state-changing meta actions. These
+ // are all handled locally.
+
+ // ------------------------------------------------------------
+
+ case META_PUSH_ACTION:
+ {
+ MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
+ pushState( rStates,
+ pPushAction->GetFlags() );
+ }
+ break;
+
+ case META_POP_ACTION:
+ popState( rStates );
+ break;
+
+ case META_TEXTLANGUAGE_ACTION:
+ // FALLTHROUGH intended
+ case META_REFPOINT_ACTION:
+ // handled via pCurrAct->Execute( &rVDev )
+ break;
+
+ case META_MAPMODE_ACTION:
+ // modify current mapModeTransformation
+ // transformation, such that subsequent
+ // coordinates map correctly
+ tools::calcLogic2PixelAffineTransform( getState( rStates ).mapModeTransform,
+ rVDev );
+ break;
+
+ // monitor clip regions, to assemble clip polygon on our own
+ case META_CLIPREGION_ACTION:
+ {
+ MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
+
+ if( !pClipAction->IsClipping() )
+ {
+ // clear clipping
+ getState( rStates ).clip.clear();
+ }
+ else
+ {
+ if( !pClipAction->GetRegion().HasPolyPolygon() )
+ {
+ VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
+ "region encountered, falling back to bounding box!" );
+
+ // #121806# explicitely kept integer
+ Rectangle aClipRect(
+ rVDev.LogicToPixel(
+ pClipAction->GetRegion().GetBoundRect() ) );
+
+ // intersect current clip with given rect
+ updateClipping(
+ aClipRect,
+ rFactoryParms,
+ false );
+ }
+ else
+ {
+ // set new clip polygon (don't intersect
+ // with old one, just set it)
+
+ // #121806# explicitely kept integer
+ updateClipping(
+ rVDev.LogicToPixel(
+ pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
+ rFactoryParms,
+ false );
+ }
+ }
+
+ break;
+ }
+
+ case META_ISECTRECTCLIPREGION_ACTION:
+ {
+ MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
+
+ // #121806# explicitely kept integer
+ Rectangle aClipRect(
+ rVDev.LogicToPixel( pClipAction->GetRect() ) );
+
+ // intersect current clip with given rect
+ updateClipping(
+ aClipRect,
+ rFactoryParms,
+ true );
+
+ break;
+ }
+
+ case META_ISECTREGIONCLIPREGION_ACTION:
+ {
+ MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
+
+ if( !pClipAction->GetRegion().HasPolyPolygon() )
+ {
+ VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
+ "region encountered, falling back to bounding box!" );
+
+ // #121806# explicitely kept integer
+ Rectangle aClipRect(
+ rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
+
+ // intersect current clip with given rect
+ updateClipping(
+ aClipRect,
+ rFactoryParms,
+ true );
+ }
+ else
+ {
+ // intersect current clip with given clip polygon
+
+ // #121806# explicitely kept integer
+ updateClipping(
+ rVDev.LogicToPixel(
+ pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
+ rFactoryParms,
+ true );
+ }
+
+ break;
+ }
+
+ case META_MOVECLIPREGION_ACTION:
+ // TODO(F2): NYI
+ break;
+
+ case META_LINECOLOR_ACTION:
+ if( !rParms.maLineColor.is_initialized() )
+ {
+ setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
+ getState( rStates ).isLineColorSet,
+ getState( rStates ).lineColor,
+ rCanvas );
+ }
+ break;
+
+ case META_FILLCOLOR_ACTION:
+ if( !rParms.maFillColor.is_initialized() )
+ {
+ setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
+ getState( rStates ).isFillColorSet,
+ getState( rStates ).fillColor,
+ rCanvas );
+ }
+ break;
+
+ case META_TEXTCOLOR_ACTION:
+ {
+ if( !rParms.maTextColor.is_initialized() )
+ {
+ // Text color is set unconditionally, thus, no
+ // use of setStateColor here
+ ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
+
+ // force alpha part of color to
+ // opaque. transparent painting is done
+ // explicitely via META_TRANSPARENT_ACTION
+ aColor.SetTransparency(0);
+
+ getState( rStates ).textColor =
+ ::vcl::unotools::colorToDoubleSequence(
+ aColor,
+ rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
+ }
+ }
+ break;
+
+ case META_TEXTFILLCOLOR_ACTION:
+ if( !rParms.maTextColor.is_initialized() )
+ {
+ setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
+ getState( rStates ).isTextFillColorSet,
+ getState( rStates ).textFillColor,
+ rCanvas );
+ }
+ break;
+
+ case META_TEXTLINECOLOR_ACTION:
+ if( !rParms.maTextColor.is_initialized() )
+ {
+ setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
+ getState( rStates ).isTextLineColorSet,
+ getState( rStates ).textLineColor,
+ rCanvas );
+ }
+ break;
+
+ case META_TEXTALIGN_ACTION:
+ {
+ ::cppcanvas::internal::OutDevState& rState = getState( rStates );
+ const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
+
+ rState.textReferencePoint = eTextAlign;
+ }
+ break;
+
+ case META_FONT_ACTION:
+ {
+ ::cppcanvas::internal::OutDevState& rState = getState( rStates );
+ const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
+
+ rState.xFont = createFont( rState.fontRotation,
+ rFont,
+ rFactoryParms );
+
+ // TODO(Q2): define and use appropriate enumeration types
+ rState.textReliefStyle = (sal_Int8)rFont.GetRelief();
+ rState.textOverlineStyle = (sal_Int8)rFont.GetOverline();
+ rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ?
+ (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
+ (sal_Int8)rFont.GetUnderline();
+ rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout();
+ rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark();
+ rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False);
+ rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False);
+ rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False);
+ }
+ break;
+
+ case META_RASTEROP_ACTION:
+ // TODO(F2): NYI
+ break;
+
+ case META_LAYOUTMODE_ACTION:
+ {
+ // TODO(F2): A lot is missing here
+ int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
+ ::cppcanvas::internal::OutDevState& rState = getState( rStates );
+ switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
+ {
+ case TEXT_LAYOUT_BIDI_LTR:
+ rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
+ break;
+
+ case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
+ rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
+ break;
+
+ case TEXT_LAYOUT_BIDI_RTL:
+ rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
+ break;
+
+ case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
+ rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
+ break;
+ }
+
+ rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
+ if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
+ && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
+ {
+ rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
+ }
+ }
+ break;
+
+ // ------------------------------------------------------------
+
+ // In the second part of this monster-switch, we
+ // handle all recursing meta actions. These are the
+ // ones generating a metafile by themselves, which is
+ // then processed by recursively calling this method.
+
+ // ------------------------------------------------------------
+
+ case META_GRADIENT_ACTION:
+ {
+ MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
+ createGradientAction( ::Polygon( pGradAct->GetRect() ),
+ pGradAct->GetGradient(),
+ rFactoryParms,
+ true,
+ bSubsettableActions );
+ }
+ break;
+
+ case META_HATCH_ACTION:
+ {
+ // TODO(F2): use native Canvas hatches here
+ GDIMetaFile aTmpMtf;
+
+ rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
+ static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
+ aTmpMtf );
+ createActions( aTmpMtf, rFactoryParms,
+ bSubsettableActions );
+ }
+ break;
+
+ case META_EPS_ACTION:
+ {
+ MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct);
+ const GDIMetaFile& rSubstitute = pAct->GetSubstitute();
+
+ // #121806# explicitely kept integer
+ const Size aMtfSize( rSubstitute.GetPrefSize() );
+ const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
+ rSubstitute.GetPrefMapMode() ) );
+
+ // #i44110# correct null-sized output - there
+ // are metafiles which have zero size in at
+ // least one dimension
+ const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
+ ::std::max( aMtfSizePixPre.Height(), 1L ) );
+
+ // Setup local transform, such that the
+ // metafile renders itself into the given
+ // output rectangle
+ pushState( rStates, PUSH_ALL );
+
+ rVDev.Push();
+ rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
+
+ const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
+ const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
+
+ getState( rStates ).transform.translate( rPos.X(),
+ rPos.Y() );
+ getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
+ (double)rSize.Height() / aMtfSizePix.Height() );
+
+ createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
+ rFactoryParms,
+ bSubsettableActions );
+
+ rVDev.Pop();
+ popState( rStates );
+ }
+ break;
+
+ // handle metafile comments, to retrieve
+ // meta-information for gradients, fills and
+ // strokes. May skip actions, and may recurse.
+ case META_COMMENT_ACTION:
+ {
+ MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
+
+ // Handle gradients
+ if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
+ {
+ MetaGradientExAction* pGradAction = NULL;
+ bool bDone( false );
+ while( !bDone &&
+ (pCurrAct=rMtf.NextAction()) != NULL )
+ {
+ switch( pCurrAct->GetType() )
+ {
+ // extract gradient info
+ case META_GRADIENTEX_ACTION:
+ pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
+ break;
+
+ // skip broken-down rendering, output gradient when sequence is ended
+ case META_COMMENT_ACTION:
+ if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL )
+ {
+ bDone = true;
+
+ if( pGradAction )
+ {
+ createGradientAction( pGradAction->GetPolyPolygon(),
+ pGradAction->GetGradient(),
+ rFactoryParms,
+ false,
+ bSubsettableActions );
+ }
+ }
+ break;
+ }
+ }
+ }
+ // TODO(P2): Handle drawing layer strokes, via
+ // XPATHSTROKE_SEQ_BEGIN comment
+
+ // Handle drawing layer fills
+ else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) )
+ {
+ const sal_uInt8* pData = pAct->GetData();
+ if ( pData )
+ {
+ SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
+
+ SvtGraphicFill aFill;
+ aMemStm >> aFill;
+
+ // TODO(P2): Also handle gradients and
+ // hatches like this
+
+ // only evaluate comment for pure
+ // bitmap fills. If a transparency
+ // gradient is involved (denoted by
+ // the FloatTransparent action), take
+ // the normal meta actions.
+ if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
+ !isActionContained( rMtf,
+ "XPATHFILL_SEQ_END",
+ META_FLOATTRANSPARENT_ACTION ) )
+ {
+ rendering::Texture aTexture;
+
+ // TODO(F1): the SvtGraphicFill
+ // can also transport metafiles
+ // here, handle that case, too
+ Graphic aGraphic;
+ aFill.getGraphic( aGraphic );
+
+ BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
+ const ::Size aBmpSize( aBmpEx.GetSizePixel() );
+
+ ::SvtGraphicFill::Transform aTransform;
+ aFill.getTransform( aTransform );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+
+ // convert to basegfx matrix
+ aMatrix.set(0,0, aTransform.matrix[ 0 ] );
+ aMatrix.set(0,1, aTransform.matrix[ 1 ] );
+ aMatrix.set(0,2, aTransform.matrix[ 2 ] );
+ aMatrix.set(1,0, aTransform.matrix[ 3 ] );
+ aMatrix.set(1,1, aTransform.matrix[ 4 ] );
+ aMatrix.set(1,2, aTransform.matrix[ 5 ] );
+
+ ::basegfx::B2DHomMatrix aScale;
+ aScale.scale( aBmpSize.Width(),
+ aBmpSize.Height() );
+
+ // post-multiply with the bitmap
+ // size (XCanvas' texture assumes
+ // the given bitmap to be
+ // normalized to [0,1]x[0,1]
+ // rectangle)
+ aMatrix = aMatrix * aScale;
+
+ // pre-multiply with the
+ // logic-to-pixel scale factor
+ // (the metafile comment works in
+ // logical coordinates).
+ ::basegfx::B2DHomMatrix aLogic2PixelTransform;
+ aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
+ rVDev );
+
+ ::basegfx::unotools::affineMatrixFromHomMatrix(
+ aTexture.AffineTransform,
+ aMatrix );
+
+ aTexture.Alpha = 1.0 - aFill.getTransparency();
+ aTexture.Bitmap =
+ ::vcl::unotools::xBitmapFromBitmapEx(
+ rCanvas->getUNOCanvas()->getDevice(),
+ aBmpEx );
+ if( aFill.isTiling() )
+ {
+ aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
+ aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
+ }
+ else
+ {
+ aTexture.RepeatModeX = rendering::TexturingMode::NONE;
+ aTexture.RepeatModeY = rendering::TexturingMode::NONE;
+ }
+
+ ::PolyPolygon aPath;
+ aFill.getPath( aPath );
+
+ ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
+ aPoly.transform( getState( rStates ).mapModeTransform );
+ ActionSharedPtr pPolyAction(
+ internal::PolyPolyActionFactory::createPolyPolyAction(
+ aPoly,
+ rCanvas,
+ getState( rStates ),
+ aTexture ) );
+
+ if( pPolyAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPolyAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pPolyAction->getActionCount()-1;
+ }
+
+ // skip broken-down render output
+ skipContent( rMtf,
+ "XPATHFILL_SEQ_END",
+ io_rCurrActionIndex );
+ }
+ }
+ }
+ }
+ break;
+
+ // ------------------------------------------------------------
+
+ // In the third part of this monster-switch, we
+ // handle all 'acting' meta actions. These are all
+ // processed by constructing function objects for
+ // them, which will later ease caching.
+
+ // ------------------------------------------------------------
+
+ case META_POINT_ACTION:
+ {
+ const OutDevState& rState( getState( rStates ) );
+ if( rState.lineColor.getLength() )
+ {
+ ActionSharedPtr pPointAction(
+ internal::PointActionFactory::createPointAction(
+ rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
+ static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
+ rCanvas,
+ rState ) );
+
+ if( pPointAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPointAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pPointAction->getActionCount()-1;
+ }
+ }
+ }
+ break;
+
+ case META_PIXEL_ACTION:
+ {
+ const OutDevState& rState( getState( rStates ) );
+ if( rState.lineColor.getLength() )
+ {
+ ActionSharedPtr pPointAction(
+ internal::PointActionFactory::createPointAction(
+ rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
+ static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
+ rCanvas,
+ rState,
+ static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
+
+ if( pPointAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPointAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pPointAction->getActionCount()-1;
+ }
+ }
+ }
+ break;
+
+ case META_LINE_ACTION:
+ {
+ const OutDevState& rState( getState( rStates ) );
+ if( rState.lineColor.getLength() )
+ {
+ MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
+
+ const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
+
+ const ::basegfx::B2DPoint aStartPoint(
+ rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
+ const ::basegfx::B2DPoint aEndPoint(
+ rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
+
+ ActionSharedPtr pLineAction;
+
+ if( rLineInfo.IsDefault() )
+ {
+ // plain hair line
+ pLineAction =
+ internal::LineActionFactory::createLineAction(
+ aStartPoint,
+ aEndPoint,
+ rCanvas,
+ rState );
+
+ if( pLineAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pLineAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pLineAction->getActionCount()-1;
+ }
+ }
+ else if( LINE_NONE != rLineInfo.GetStyle() )
+ {
+ // 'thick' line
+ rendering::StrokeAttributes aStrokeAttributes;
+
+ setupStrokeAttributes( aStrokeAttributes,
+ rFactoryParms,
+ rLineInfo );
+
+ // XCanvas can only stroke polygons,
+ // not simple lines - thus, handle
+ // this case via the polypolygon
+ // action
+ ::basegfx::B2DPolygon aPoly;
+ aPoly.append( aStartPoint );
+ aPoly.append( aEndPoint );
+ pLineAction =
+ internal::PolyPolyActionFactory::createPolyPolyAction(
+ ::basegfx::B2DPolyPolygon( aPoly ),
+ rCanvas, rState, aStrokeAttributes );
+
+ if( pLineAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pLineAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pLineAction->getActionCount()-1;
+ }
+ }
+ // else: line style is default
+ // (i.e. invisible), don't generate action
+ }
+ }
+ break;
+
+ case META_RECT_ACTION:
+ {
+ const Rectangle& rRect(
+ static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
+
+ if( rRect.IsEmpty() )
+ break;
+
+ const OutDevState& rState( getState( rStates ) );
+ const ::basegfx::B2DPoint aTopLeftPixel(
+ rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
+ const ::basegfx::B2DPoint aBottomRightPixel(
+ rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
+ // #121100# OutputDevice::DrawRect() fills
+ // rectangles Apple-like, i.e. with one
+ // additional pixel to the right and bottom.
+ ::basegfx::B2DPoint(1,1) );
+
+ createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
+ ::basegfx::B2DRange( aTopLeftPixel,
+ aBottomRightPixel )),
+ rFactoryParms );
+ break;
+ }
+
+ case META_ROUNDRECT_ACTION:
+ {
+ const Rectangle& rRect(
+ static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
+
+ if( rRect.IsEmpty() )
+ break;
+
+ ::basegfx::B2DPolygon aPoly(
+ ::basegfx::tools::createPolygonFromRect(
+ ::basegfx::B2DRange(
+ ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
+ ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
+ ::basegfx::B2DPoint(1,1) ),
+ static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(),
+ static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ));
+ aPoly.transform( getState( rStates ).mapModeTransform );
+
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_ELLIPSE_ACTION:
+ {
+ const Rectangle& rRect(
+ static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
+
+ if( rRect.IsEmpty() )
+ break;
+
+ const ::basegfx::B2DRange aRange(
+ ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
+ ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
+ ::basegfx::B2DPoint(1,1) );
+
+ ::basegfx::B2DPolygon aPoly(
+ ::basegfx::tools::createPolygonFromEllipse(
+ aRange.getCenter(),
+ aRange.getWidth(),
+ aRange.getHeight() ));
+ aPoly.transform( getState( rStates ).mapModeTransform );
+
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_ARC_ACTION:
+ {
+ // TODO(F1): Missing basegfx functionality. Mind empty rects!
+ const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
+ static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
+ static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
+ ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
+ aPoly.transform( getState( rStates ).mapModeTransform );
+
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_PIE_ACTION:
+ {
+ // TODO(F1): Missing basegfx functionality. Mind empty rects!
+ const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
+ static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
+ static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
+ ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
+ aPoly.transform( getState( rStates ).mapModeTransform );
+
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_CHORD_ACTION:
+ {
+ // TODO(F1): Missing basegfx functionality. Mind empty rects!
+ const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
+ static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
+ static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
+ ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
+ aPoly.transform( getState( rStates ).mapModeTransform );
+
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_POLYLINE_ACTION:
+ {
+ const OutDevState& rState( getState( rStates ) );
+ if( rState.lineColor.getLength() ||
+ rState.fillColor.getLength() )
+ {
+ MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
+
+ const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
+ ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
+ aPoly.transform( rState.mapModeTransform );
+
+ ActionSharedPtr pLineAction;
+
+ if( rLineInfo.IsDefault() )
+ {
+ // plain hair line polygon
+ pLineAction =
+ internal::PolyPolyActionFactory::createLinePolyPolyAction(
+ ::basegfx::B2DPolyPolygon(aPoly),
+ rCanvas,
+ rState );
+
+ if( pLineAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pLineAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pLineAction->getActionCount()-1;
+ }
+ }
+ else if( LINE_NONE != rLineInfo.GetStyle() )
+ {
+ // 'thick' line polygon
+ rendering::StrokeAttributes aStrokeAttributes;
+
+ setupStrokeAttributes( aStrokeAttributes,
+ rFactoryParms,
+ rLineInfo );
+
+ pLineAction =
+ internal::PolyPolyActionFactory::createPolyPolyAction(
+ ::basegfx::B2DPolyPolygon(aPoly),
+ rCanvas,
+ rState,
+ aStrokeAttributes ) ;
+
+ if( pLineAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pLineAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pLineAction->getActionCount()-1;
+ }
+ }
+ // else: line style is default
+ // (i.e. invisible), don't generate action
+ }
+ }
+ break;
+
+ case META_POLYGON_ACTION:
+ {
+ ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
+ aPoly.transform( getState( rStates ).mapModeTransform );
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_POLYPOLYGON_ACTION:
+ {
+ ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
+ aPoly.transform( getState( rStates ).mapModeTransform );
+ createFillAndStroke( aPoly,
+ rFactoryParms );
+ }
+ break;
+
+ case META_BMP_ACTION:
+ {
+ MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ pAct->GetBitmap(),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_BMPSCALE_ACTION:
+ {
+ MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ pAct->GetBitmap(),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_BMPSCALEPART_ACTION:
+ {
+ MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
+
+ // crop bitmap to given source rectangle (no
+ // need to copy and convert the whole bitmap)
+ Bitmap aBmp( pAct->GetBitmap() );
+ const Rectangle aCropRect( pAct->GetSrcPoint(),
+ pAct->GetSrcSize() );
+ aBmp.Crop( aCropRect );
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ aBmp,
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_BMPEX_ACTION:
+ {
+ MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ pAct->GetBitmapEx(),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_BMPEXSCALE_ACTION:
+ {
+ MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ pAct->GetBitmapEx(),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_BMPEXSCALEPART_ACTION:
+ {
+ MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
+
+ // crop bitmap to given source rectangle (no
+ // need to copy and convert the whole bitmap)
+ BitmapEx aBmp( pAct->GetBitmapEx() );
+ const Rectangle aCropRect( pAct->GetSrcPoint(),
+ pAct->GetSrcSize() );
+ aBmp.Crop( aCropRect );
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ aBmp,
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_MASK_ACTION:
+ {
+ MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
+
+ // create masked BitmapEx right here, as the
+ // canvas does not provide equivalent
+ // functionality
+ BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
+ pAct->GetColor() ));
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ aBmp,
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_MASKSCALE_ACTION:
+ {
+ MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
+
+ // create masked BitmapEx right here, as the
+ // canvas does not provide equivalent
+ // functionality
+ BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
+ pAct->GetColor() ));
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ aBmp,
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_MASKSCALEPART_ACTION:
+ {
+ MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
+
+ // create masked BitmapEx right here, as the
+ // canvas does not provide equivalent
+ // functionality
+ BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
+ pAct->GetColor() ));
+
+ // crop bitmap to given source rectangle (no
+ // need to copy and convert the whole bitmap)
+ const Rectangle aCropRect( pAct->GetSrcPoint(),
+ pAct->GetSrcSize() );
+ aBmp.Crop( aCropRect );
+
+ ActionSharedPtr pBmpAction(
+ internal::BitmapActionFactory::createBitmapAction(
+ aBmp,
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pBmpAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pBmpAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pBmpAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_GRADIENTEX_ACTION:
+ // TODO(F1): use native Canvas gradients here
+ // action is ignored here, because redundant to META_GRADIENT_ACTION
+ break;
+
+ case META_WALLPAPER_ACTION:
+ // TODO(F2): NYI
+ break;
+
+ case META_TRANSPARENT_ACTION:
+ {
+ const OutDevState& rState( getState( rStates ) );
+ if( rState.lineColor.getLength() ||
+ rState.fillColor.getLength() )
+ {
+ MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
+ ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
+ aPoly.transform( rState.mapModeTransform );
+
+ ActionSharedPtr pPolyAction(
+ internal::PolyPolyActionFactory::createPolyPolyAction(
+ aPoly,
+ rCanvas,
+ rState,
+ pAct->GetTransparence() ) );
+
+ if( pPolyAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPolyAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pPolyAction->getActionCount()-1;
+ }
+ }
+ }
+ break;
+
+ case META_FLOATTRANSPARENT_ACTION:
+ {
+ MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
+
+ internal::MtfAutoPtr pMtf(
+ new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
+
+ // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
+ internal::GradientAutoPtr pGradient(
+ new Gradient( pAct->GetGradient() ) );
+
+ DBG_TESTSOLARMUTEX();
+
+ ActionSharedPtr pFloatTransAction(
+ internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
+ pMtf,
+ pGradient,
+ rParms,
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
+ getState( rStates ).mapModeTransform *
+ ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
+ rCanvas,
+ getState( rStates ) ) );
+
+ if( pFloatTransAction )
+ {
+ maActions.push_back(
+ MtfAction(
+ pFloatTransAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_TEXT_ACTION:
+ {
+ MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
+ XubString sText = XubString( pAct->GetText() );
+
+ if( rVDev.GetDigitLanguage())
+ convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
+
+ createTextAction(
+ pAct->GetPoint(),
+ sText,
+ pAct->GetIndex(),
+ pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
+ NULL,
+ rFactoryParms,
+ bSubsettableActions );
+ }
+ break;
+
+ case META_TEXTARRAY_ACTION:
+ {
+ MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
+ XubString sText = XubString( pAct->GetText() );
+
+ if( rVDev.GetDigitLanguage())
+ convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
+
+ createTextAction(
+ pAct->GetPoint(),
+ sText,
+ pAct->GetIndex(),
+ pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
+ pAct->GetDXArray(),
+ rFactoryParms,
+ bSubsettableActions );
+ }
+ break;
+
+ case META_TEXTLINE_ACTION:
+ {
+ MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct);
+
+ const OutDevState& rState( getState( rStates ) );
+ const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
+ rVDev ) );
+ const ::Point aStartPoint( pAct->GetStartPoint() );
+ const ::basegfx::B2DSize aSize( rState.mapModeTransform *
+ ::basegfx::B2DSize(pAct->GetWidth(),
+ 0 ));
+
+ ActionSharedPtr pPolyAction(
+ PolyPolyActionFactory::createPolyPolyAction(
+ tools::createTextLinesPolyPolygon(
+ rState.mapModeTransform *
+ ::basegfx::B2DPoint(
+ ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
+ ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
+ aSize.getX(),
+ tools::createTextLineInfo( rVDev,
+ rState )),
+ rCanvas,
+ rState ) );
+
+ if( pPolyAction.get() )
+ {
+ maActions.push_back(
+ MtfAction(
+ pPolyAction,
+ io_rCurrActionIndex ) );
+
+ io_rCurrActionIndex += pPolyAction->getActionCount()-1;
+ }
+ }
+ break;
+
+ case META_TEXTRECT_ACTION:
+ {
+ MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
+
+ pushState( rStates, PUSH_ALL );
+
+ // use the VDev to break up the text rect
+ // action into readily formatted lines
+ GDIMetaFile aTmpMtf;
+ rVDev.AddTextRectActions( pAct->GetRect(),
+ pAct->GetText(),
+ pAct->GetStyle(),
+ aTmpMtf );
+
+ createActions( aTmpMtf,
+ rFactoryParms,
+ bSubsettableActions );
+
+ popState( rStates );
+
+ break;
+ }
+
+ case META_STRETCHTEXT_ACTION:
+ {
+ MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
+ XubString sText = XubString( pAct->GetText() );
+
+ if( rVDev.GetDigitLanguage())
+ convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
+
+ const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ?
+ pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() );
+
+ // #i70897# Nothing to do, actually...
+ if( nLen == 0 )
+ break;
+
+ // have to fit the text into the given
+ // width. This is achieved by internally
+ // generating a DX array, and uniformly
+ // distributing the excess/insufficient width
+ // to every logical character.
+ ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
+
+ rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
+ pAct->GetIndex(), pAct->GetLen() );
+
+ const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
+
+ // Last entry of pDXArray contains total width of the text
+ sal_Int32* p=pDXArray.get();
+ for( sal_uInt16 i=1; i<=nLen; ++i )
+ {
+ // calc ratio for every array entry, to
+ // distribute rounding errors 'evenly'
+ // across the characters. Note that each
+ // entry represents the 'end' position of
+ // the corresponding character, thus, we
+ // let i run from 1 to nLen.
+ *p++ += (sal_Int32)i*nWidthDifference/nLen;
+ }
+
+ createTextAction(
+ pAct->GetPoint(),
+ sText,
+ pAct->GetIndex(),
+ pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
+ pDXArray.get(),
+ rFactoryParms,
+ bSubsettableActions );
+ }
+ break;
+
+ default:
+ OSL_ENSURE( false,
+ "Unknown meta action type encountered" );
+ break;
+ }
+
+ // increment action index (each mtf action counts _at
+ // least_ one. Some count for more, therefore,
+ // io_rCurrActionIndex is sometimes incremented by
+ // pAct->getActionCount()-1 above, the -1 being the
+ // correction for the unconditional increment here).
+ ++io_rCurrActionIndex;
+ }
+
+ return true;
+ }
+
+
+ namespace
+ {
+ class ActionRenderer
+ {
+ public:
+ ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
+ maTransformation( rTransformation ),
+ mbRet( true )
+ {
+ }
+
+ bool result()
+ {
+ return mbRet;
+ }
+
+ void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
+ {
+ // ANDing the result. We want to fail if at least
+ // one action failed.
+ mbRet &= rAction.mpAction->render( maTransformation );
+ }
+
+ void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
+ const Action::Subset& rSubset )
+ {
+ // ANDing the result. We want to fail if at least
+ // one action failed.
+ mbRet &= rAction.mpAction->render( maTransformation,
+ rSubset );
+ }
+
+ private:
+ ::basegfx::B2DHomMatrix maTransformation;
+ bool mbRet;
+ };
+
+ class AreaQuery
+ {
+ public:
+ AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
+ maTransformation( rTransformation ),
+ maBounds()
+ {
+ }
+
+ bool result()
+ {
+ return true; // nothing can fail here
+ }
+
+ void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
+ {
+ maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
+ }
+
+ void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction,
+ const Action::Subset& rSubset )
+ {
+ maBounds.expand( rAction.mpAction->getBounds( maTransformation,
+ rSubset ) );
+ }
+
+ ::basegfx::B2DRange getBounds() const
+ {
+ return maBounds;
+ }
+
+ private:
+ ::basegfx::B2DHomMatrix maTransformation;
+ ::basegfx::B2DRange maBounds;
+ };
+
+ // Doing that via inline class. Compilers tend to not inline free
+ // functions.
+ struct UpperBoundActionIndexComparator
+ {
+ bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
+ const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
+ {
+ const sal_Int32 nLHSCount( rLHS.mpAction ?
+ rLHS.mpAction->getActionCount() : 0 );
+ const sal_Int32 nRHSCount( rRHS.mpAction ?
+ rRHS.mpAction->getActionCount() : 0 );
+
+ // compare end of action range, to have an action selected
+ // by lower_bound even if the requested index points in
+ // the middle of the action's range
+ return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
+ }
+ };
+
+ /** Algorithm to apply given functor to a subset range
+
+ @tpl Functor
+
+ Functor to call for each element of the subset
+ range. Must provide the following method signatures:
+ bool result() (returning false if operation failed)
+
+ */
+ template< typename Functor > bool
+ forSubsetRange( Functor& rFunctor,
+ ImplRenderer::ActionVector::const_iterator aRangeBegin,
+ ImplRenderer::ActionVector::const_iterator aRangeEnd,
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex,
+ const ImplRenderer::ActionVector::const_iterator& rEnd )
+ {
+ if( aRangeBegin == aRangeEnd )
+ {
+ // only a single action. Setup subset, and call functor
+ Action::Subset aSubset;
+ aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
+ nStartIndex - aRangeBegin->mnOrigIndex );
+ aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(),
+ nEndIndex - aRangeBegin->mnOrigIndex );
+
+ ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
+ "ImplRenderer::forSubsetRange(): Invalid indices" );
+
+ rFunctor( *aRangeBegin, aSubset );
+ }
+ else
+ {
+ // more than one action.
+
+ // render partial first, full intermediate, and
+ // partial last action
+ Action::Subset aSubset;
+ aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
+ nStartIndex - aRangeBegin->mnOrigIndex );
+ aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount();
+
+ ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
+ "ImplRenderer::forSubsetRange(): Invalid indices" );
+
+ rFunctor( *aRangeBegin, aSubset );
+
+ // first action rendered, skip to next
+ ++aRangeBegin;
+
+ // render full middle actions
+ while( aRangeBegin != aRangeEnd )
+ rFunctor( *aRangeBegin++ );
+
+ if( aRangeEnd == rEnd ||
+ aRangeEnd->mnOrigIndex > nEndIndex )
+ {
+ // aRangeEnd denotes end of action vector,
+ //
+ // or
+ //
+ // nEndIndex references something _after_
+ // aRangeBegin, but _before_ aRangeEnd
+ //
+ // either way: no partial action left
+ return rFunctor.result();
+ }
+
+ aSubset.mnSubsetBegin = 0;
+ aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex;
+
+ ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
+ "ImplRenderer::forSubsetRange(): Invalid indices" );
+
+ rFunctor( *aRangeEnd, aSubset );
+ }
+
+ return rFunctor.result();
+ }
+ }
+
+ bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex,
+ sal_Int32& io_rEndIndex,
+ ActionVector::const_iterator& o_rRangeBegin,
+ ActionVector::const_iterator& o_rRangeEnd ) const
+ {
+ ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
+ "ImplRenderer::getSubsetIndices(): invalid action range" );
+
+ ENSURE_OR_RETURN_FALSE( !maActions.empty(),
+ "ImplRenderer::getSubsetIndices(): no actions to render" );
+
+ const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
+ const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
+ maActions.back().mpAction->getActionCount() );
+
+ // clip given range to permissible values (there might be
+ // ranges before and behind the valid indices)
+ io_rStartIndex = ::std::max( nMinActionIndex,
+ io_rStartIndex );
+ io_rEndIndex = ::std::min( nMaxActionIndex,
+ io_rEndIndex );
+
+ if( io_rStartIndex == io_rEndIndex ||
+ io_rStartIndex > io_rEndIndex )
+ {
+ // empty range, don't render anything. The second
+ // condition e.g. happens if the requested range lies
+ // fully before or behind the valid action indices.
+ return false;
+ }
+
+
+ const ActionVector::const_iterator aBegin( maActions.begin() );
+ const ActionVector::const_iterator aEnd( maActions.end() );
+
+
+ // find start and end action
+ // =========================
+ o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
+ MtfAction( ActionSharedPtr(), io_rStartIndex ),
+ UpperBoundActionIndexComparator() );
+ o_rRangeEnd = ::std::lower_bound( aBegin, aEnd,
+ MtfAction( ActionSharedPtr(), io_rEndIndex ),
+ UpperBoundActionIndexComparator() );
+ return true;
+ }
+
+
+ // Public methods
+ // ====================================================================
+
+ ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
+ const GDIMetaFile& rMtf,
+ const Parameters& rParams ) :
+ CanvasGraphicHelper( rCanvas ),
+ maActions()
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
+
+ OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
+ "ImplRenderer::ImplRenderer(): Invalid canvas" );
+ OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
+ "ImplRenderer::ImplRenderer(): Invalid graphic device" );
+
+ // make sure canvas and graphic device are valid; action
+ // creation don't check that every time
+ if( rCanvas.get() == NULL ||
+ !rCanvas->getUNOCanvas().is() ||
+ !rCanvas->getUNOCanvas()->getDevice().is() )
+ {
+ // leave actions empty
+ return;
+ }
+
+ VectorOfOutDevStates aStateStack;
+
+ VirtualDevice aVDev;
+ aVDev.EnableOutput( sal_False );
+
+ // Setup VDev for state tracking and mapping
+ // =========================================
+
+ aVDev.SetMapMode( rMtf.GetPrefMapMode() );
+
+ const Size aMtfSize( rMtf.GetPrefSize() );
+ const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
+ rMtf.GetPrefMapMode() ) );
+ const Point aEmptyPt;
+ const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) );
+
+ // #i44110# correct null-sized output - there are shapes
+ // which have zero size in at least one dimension
+ const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
+ ::std::max( aMtfSizePixPre.Height(), 1L ) );
+
+ sal_Int32 nCurrActions(0);
+ ActionFactoryParameters aParms(aStateStack,
+ rCanvas,
+ aVDev,
+ rParams,
+ nCurrActions );
+
+ // init state stack
+ clearStateStack( aStateStack );
+
+ // Setup local state, such that the metafile renders
+ // itself into a one-by-one square at the origin for
+ // identity view and render transformations
+ getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(),
+ 1.0 / aMtfSizePix.Height() );
+
+ tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform,
+ aVDev );
+
+ ColorSharedPtr pColor( getCanvas()->createColor() );
+
+ // setup default text color to black
+ getState( aStateStack ).textColor =
+ getState( aStateStack ).textFillColor =
+ getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF );
+
+ // apply overrides from the Parameters struct
+ if( rParams.maFillColor.is_initialized() )
+ {
+ getState( aStateStack ).isFillColorSet = true;
+ getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor );
+ }
+ if( rParams.maLineColor.is_initialized() )
+ {
+ getState( aStateStack ).isLineColorSet = true;
+ getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor );
+ }
+ if( rParams.maTextColor.is_initialized() )
+ {
+ getState( aStateStack ).isTextFillColorSet = true;
+ getState( aStateStack ).isTextLineColorSet = true;
+ getState( aStateStack ).textColor =
+ getState( aStateStack ).textFillColor =
+ getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
+ }
+ if( rParams.maFontName.is_initialized() ||
+ rParams.maFontWeight.is_initialized() ||
+ rParams.maFontLetterForm.is_initialized() ||
+ rParams.maFontUnderline.is_initialized() )
+ {
+ ::cppcanvas::internal::OutDevState& rState = getState( aStateStack );
+
+ rState.xFont = createFont( rState.fontRotation,
+ ::Font(), // default font
+ aParms );
+ }
+
+ createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
+ // we're
+ // changing
+ // the
+ // current
+ // action
+ // in
+ // createActions!
+ aParms,
+ true // TODO(P1): make subsettability configurable
+ );
+ }
+
+ ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas,
+ const BitmapEx& rBmpEx,
+ const Parameters& rParams ) :
+ CanvasGraphicHelper( rCanvas ),
+ maActions()
+ {
+ // TODO(F3): property modification parameters are
+ // currently ignored for Bitmaps
+ (void)rParams;
+
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" );
+
+ OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
+ "ImplRenderer::ImplRenderer(): Invalid canvas" );
+ OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
+ "ImplRenderer::ImplRenderer(): Invalid graphic device" );
+
+ // make sure canvas and graphic device are valid; action
+ // creation don't check that every time
+ if( rCanvas.get() == NULL ||
+ !rCanvas->getUNOCanvas().is() ||
+ !rCanvas->getUNOCanvas()->getDevice().is() )
+ {
+ // leave actions empty
+ return;
+ }
+
+ OutDevState aState;
+
+ const Size aBmpSize( rBmpEx.GetSizePixel() );
+
+ // Setup local state, such that the bitmap renders itself
+ // into a one-by-one square for identity view and render
+ // transformations
+ aState.transform.scale( 1.0 / aBmpSize.Width(),
+ 1.0 / aBmpSize.Height() );
+
+ // create a single action for the provided BitmapEx
+ maActions.push_back(
+ MtfAction(
+ BitmapActionFactory::createBitmapAction(
+ rBmpEx,
+ ::basegfx::B2DPoint(),
+ rCanvas,
+ aState),
+ 0 ) );
+ }
+
+ ImplRenderer::~ImplRenderer()
+ {
+ }
+
+ bool ImplRenderer::drawSubset( sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
+
+ ActionVector::const_iterator aRangeBegin;
+ ActionVector::const_iterator aRangeEnd;
+
+ try
+ {
+ if( !getSubsetIndices( nStartIndex, nEndIndex,
+ aRangeBegin, aRangeEnd ) )
+ return true; // nothing to render (but _that_ was successful)
+
+ // now, aRangeBegin references the action in which the
+ // subset rendering must start, and aRangeEnd references
+ // the action in which the subset rendering must end (it
+ // might also end right at the start of the referenced
+ // action, such that zero of that action needs to be
+ // rendered).
+
+
+ // render subset of actions
+ // ========================
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::getRenderStateTransform( aMatrix,
+ getRenderState() );
+
+ ActionRenderer aRenderer( aMatrix );
+
+ return forSubsetRange( aRenderer,
+ aRangeBegin,
+ aRangeEnd,
+ nStartIndex,
+ nEndIndex,
+ maActions.end() );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_ENSURE( false,
+ rtl::OUStringToOString(
+ comphelper::anyToString( cppu::getCaughtException() ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ // convert error to return value
+ return false;
+ }
+ }
+
+ ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
+
+ ActionVector::const_iterator aRangeBegin;
+ ActionVector::const_iterator aRangeEnd;
+
+ if( !getSubsetIndices( nStartIndex, nEndIndex,
+ aRangeBegin, aRangeEnd ) )
+ return ::basegfx::B2DRange(); // nothing to render -> empty range
+
+ // now, aRangeBegin references the action in which the
+ // subset querying must start, and aRangeEnd references
+ // the action in which the subset querying must end (it
+ // might also end right at the start of the referenced
+ // action, such that zero of that action needs to be
+ // queried).
+
+
+ // query bounds for subset of actions
+ // ==================================
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::getRenderStateTransform( aMatrix,
+ getRenderState() );
+
+ AreaQuery aQuery( aMatrix );
+ forSubsetRange( aQuery,
+ aRangeBegin,
+ aRangeEnd,
+ nStartIndex,
+ nEndIndex,
+ maActions.end() );
+
+ return aQuery.getBounds();
+ }
+
+ bool ImplRenderer::draw() const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::getRenderStateTransform( aMatrix,
+ getRenderState() );
+
+ try
+ {
+ return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
+ }
+ catch( uno::Exception& )
+ {
+ OSL_ENSURE( false,
+ rtl::OUStringToOString(
+ comphelper::anyToString( cppu::getCaughtException() ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ return false;
+ }
+ }
+ }
+}