summaryrefslogtreecommitdiff
path: root/cppcanvas/source/mtfrenderer/textaction.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'cppcanvas/source/mtfrenderer/textaction.cxx')
-rw-r--r--cppcanvas/source/mtfrenderer/textaction.cxx2315
1 files changed, 2315 insertions, 0 deletions
diff --git a/cppcanvas/source/mtfrenderer/textaction.cxx b/cppcanvas/source/mtfrenderer/textaction.cxx
new file mode 100644
index 000000000000..bc4cf6688ca3
--- /dev/null
+++ b/cppcanvas/source/mtfrenderer/textaction.cxx
@@ -0,0 +1,2315 @@
+/*************************************************************************
+ *
+ * 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 <rtl/logfile.hxx>
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include <tools/gen.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/virdev.hxx>
+
+#include <basegfx/tools/canvastools.hxx>
+#include <canvas/canvastools.hxx>
+
+#include <boost/scoped_array.hpp>
+#include <boost/bind.hpp>
+#include <boost/utility.hpp>
+
+#include "textaction.hxx"
+#include "outdevstate.hxx"
+#include "mtftools.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace cppcanvas
+{
+ namespace internal
+ {
+ namespace
+ {
+ void init( rendering::RenderState& o_rRenderState,
+ const ::basegfx::B2DPoint& rStartPoint,
+ const OutDevState& rState,
+ const CanvasSharedPtr& rCanvas )
+ {
+ tools::initRenderState(o_rRenderState,rState);
+
+ // #i36950# Offset clip back to origin (as it's also moved
+ // by rStartPoint)
+ // #i53964# Also take VCL font rotation into account,
+ // since this, opposed to the FontMatrix rotation
+ // elsewhere, _does_ get incorporated into the render
+ // state transform.
+ tools::modifyClip( o_rRenderState,
+ rState,
+ rCanvas,
+ rStartPoint,
+ NULL,
+ &rState.fontRotation );
+
+ basegfx::B2DHomMatrix aLocalTransformation(basegfx::tools::createRotateB2DHomMatrix(rState.fontRotation));
+ aLocalTransformation.translate( rStartPoint.getX(),
+ rStartPoint.getY() );
+ ::canvas::tools::appendToRenderState( o_rRenderState,
+ aLocalTransformation );
+
+ o_rRenderState.DeviceColor = rState.textColor;
+ }
+
+ void init( rendering::RenderState& o_rRenderState,
+ const ::basegfx::B2DPoint& rStartPoint,
+ const OutDevState& rState,
+ const CanvasSharedPtr& rCanvas,
+ const ::basegfx::B2DHomMatrix& rTextTransform )
+ {
+ init( o_rRenderState, rStartPoint, rState, rCanvas );
+
+ // TODO(F2): Also inversely-transform clip with
+ // rTextTransform (which is actually rather hard, as the
+ // text transform is _prepended_ to the render state)!
+
+ // prepend extra font transform to render state
+ // (prepend it, because it's interpreted in the unit
+ // rect coordinate space)
+ ::canvas::tools::prependToRenderState( o_rRenderState,
+ rTextTransform );
+ }
+
+ void init( rendering::RenderState& o_rRenderState,
+ uno::Reference< rendering::XCanvasFont >& o_rFont,
+ const ::basegfx::B2DPoint& rStartPoint,
+ const OutDevState& rState,
+ const CanvasSharedPtr& rCanvas )
+ {
+ // ensure that o_rFont is valid. It is possible that
+ // text actions are generated without previously
+ // setting a font. Then, just take a default font
+ if( !o_rFont.is() )
+ {
+ // Use completely default FontRequest
+ const rendering::FontRequest aFontRequest;
+
+ geometry::Matrix2D aFontMatrix;
+ ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
+
+ o_rFont = rCanvas->getUNOCanvas()->createFont(
+ aFontRequest,
+ uno::Sequence< beans::PropertyValue >(),
+ aFontMatrix );
+ }
+
+ init( o_rRenderState,
+ rStartPoint,
+ rState,
+ rCanvas );
+ }
+
+ void init( rendering::RenderState& o_rRenderState,
+ uno::Reference< rendering::XCanvasFont >& o_rFont,
+ const ::basegfx::B2DPoint& rStartPoint,
+ const OutDevState& rState,
+ const CanvasSharedPtr& rCanvas,
+ const ::basegfx::B2DHomMatrix& rTextTransform )
+ {
+ init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas );
+
+ // TODO(F2): Also inversely-transform clip with
+ // rTextTransform (which is actually rather hard, as the
+ // text transform is _prepended_ to the render state)!
+
+ // prepend extra font transform to render state
+ // (prepend it, because it's interpreted in the unit
+ // rect coordinate space)
+ ::canvas::tools::prependToRenderState( o_rRenderState,
+ rTextTransform );
+ }
+
+ ::basegfx::B2DPolyPolygon textLinesFromLogicalOffsets( const uno::Sequence< double >& rOffsets,
+ const tools::TextLineInfo& rTextLineInfo )
+ {
+ return tools::createTextLinesPolyPolygon(
+ 0.0,
+ // extract character cell furthest to the right
+ *(::std::max_element(
+ rOffsets.getConstArray(),
+ rOffsets.getConstArray() + rOffsets.getLength() )),
+ rTextLineInfo );
+ }
+
+ uno::Sequence< double > setupDXArray( const sal_Int32* pCharWidths,
+ sal_Int32 nLen,
+ const OutDevState& rState )
+ {
+ // convert character widths from logical units
+ uno::Sequence< double > aCharWidthSeq( nLen );
+ double* pOutputWidths( aCharWidthSeq.getArray() );
+
+ // #143885# maintain (nearly) full precision of DX
+ // array, by circumventing integer-based
+ // OutDev-mapping
+ const double nScale( rState.mapModeTransform.get(0,0) );
+ for( int i = 0; i < nLen; ++i )
+ {
+ // TODO(F2): use correct scale direction
+ *pOutputWidths++ = *pCharWidths++ * nScale;
+ }
+
+ return aCharWidthSeq;
+ }
+
+ uno::Sequence< double > setupDXArray( const ::String& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ VirtualDevice& rVDev,
+ const OutDevState& rState )
+ {
+ // no external DX array given, create one from given
+ // string
+ ::boost::scoped_array< sal_Int32 > pCharWidths( new sal_Int32[nLen] );
+
+ rVDev.GetTextArray( rText, pCharWidths.get(),
+ static_cast<USHORT>(nStartPos),
+ static_cast<USHORT>(nLen) );
+
+ return setupDXArray( pCharWidths.get(), nLen, rState );
+ }
+
+ ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint& rStartPoint,
+ const OutDevState& rState,
+ const uno::Sequence< double >& rOffsets )
+ {
+ ::basegfx::B2DPoint aLocalPoint( rStartPoint );
+
+ if( rState.textAlignment )
+ {
+ // text origin is right, not left. Modify start point
+ // accordingly, because XCanvas::drawTextLayout()
+ // always aligns left!
+
+ const double nOffset( rOffsets[ rOffsets.getLength()-1 ] );
+
+ // correct start point for rotated text: rotate around
+ // former start point
+ aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset );
+ aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset );
+ }
+
+ return aLocalPoint;
+ }
+
+ /** Perform common setup for array text actions
+
+ This method creates the XTextLayout object and
+ initializes it, e.g. with the logical advancements.
+ */
+ void initArrayAction( rendering::RenderState& o_rRenderState,
+ uno::Reference< rendering::XTextLayout >& o_rTextLayout,
+ const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix* pTextTransform )
+ {
+ ENSURE_OR_THROW( rOffsets.getLength(),
+ "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
+
+ const ::basegfx::B2DPoint aLocalStartPoint(
+ adaptStartPoint( rStartPoint, rState, rOffsets ) );
+
+ uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );
+
+ if( pTextTransform )
+ init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
+ else
+ init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
+
+ o_rTextLayout = xFont->createTextLayout(
+ rendering::StringContext( rText, nStartPos, nLen ),
+ rState.textDirection,
+ 0 );
+
+ ENSURE_OR_THROW( o_rTextLayout.is(),
+ "::cppcanvas::internal::initArrayAction(): Invalid font" );
+
+ o_rTextLayout->applyLogicalAdvancements( rOffsets );
+ }
+
+ double getLineWidth( ::VirtualDevice& rVDev,
+ const OutDevState& rState,
+ const rendering::StringContext& rStringContext )
+ {
+ // TODO(F2): use correct scale direction
+ const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
+ static_cast<USHORT>(rStringContext.StartPosition),
+ static_cast<USHORT>(rStringContext.Length) ),
+ 0 );
+
+ return (rState.mapModeTransform * aSize).getX();
+ }
+
+ uno::Sequence< double >
+ calcSubsetOffsets( rendering::RenderState& io_rRenderState,
+ double& o_rMinPos,
+ double& o_rMaxPos,
+ const uno::Reference< rendering::XTextLayout >& rOrigTextLayout,
+ const ::cppcanvas::internal::Action::Subset& rSubset )
+ {
+ ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
+ "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
+
+ uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
+ const double* pOffsets( aOrigOffsets.getConstArray() );
+
+ ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
+ "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
+
+ // TODO(F3): It currently seems that for RTL text, the
+ // DX offsets are nevertheless increasing in logical
+ // text order (I'd expect they are decreasing,
+ // mimicking the fact that the text is output
+ // right-to-left). This breaks text effects for ALL
+ // RTL languages.
+
+ // determine leftmost position in given subset range -
+ // as the DX array contains the output positions
+ // starting with the second character (the first is
+ // assumed to have output position 0), correct begin
+ // iterator.
+ const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
+ *(::std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
+ pOffsets+rSubset.mnSubsetEnd )) );
+
+ // determine rightmost position in given subset range
+ // - as the DX array contains the output positions
+ // starting with the second character (the first is
+ // assumed to have output position 0), correct begin
+ // iterator.
+ const double nMaxPos(
+ *(::std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
+ 0 : rSubset.mnSubsetBegin-1),
+ pOffsets + rSubset.mnSubsetEnd )) );
+
+
+ // adapt render state, to move text output to given offset
+ // -------------------------------------------------------
+
+ // TODO(F1): Strictly speaking, we also have to adapt
+ // the clip here, which normally should _not_ move
+ // with the output offset. Neglected for now, as it
+ // does not matter for drawing layer output
+
+ if( rSubset.mnSubsetBegin > 0 )
+ {
+ ::basegfx::B2DHomMatrix aTranslation;
+ if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical )
+ {
+ // vertical text -> offset in y direction
+ aTranslation.translate( 0.0, nMinPos );
+ }
+ else
+ {
+ // horizontal text -> offset in x direction
+ aTranslation.translate( nMinPos, 0.0 );
+ }
+
+ ::canvas::tools::appendToRenderState( io_rRenderState,
+ aTranslation );
+ }
+
+
+ // reduce DX array to given substring
+ // ----------------------------------
+
+ const sal_Int32 nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
+ uno::Sequence< double > aAdaptedOffsets( nNewElements );
+ double* pAdaptedOffsets( aAdaptedOffsets.getArray() );
+
+ // move to new output position (subtract nMinPos,
+ // which is the new '0' position), copy only the range
+ // as given by rSubset.
+ ::std::transform( pOffsets + rSubset.mnSubsetBegin,
+ pOffsets + rSubset.mnSubsetEnd,
+ pAdaptedOffsets,
+ ::boost::bind( ::std::minus<double>(),
+ _1,
+ nMinPos ) );
+
+ o_rMinPos = nMinPos;
+ o_rMaxPos = nMaxPos;
+
+ return aAdaptedOffsets;
+ }
+
+ uno::Reference< rendering::XTextLayout >
+ createSubsetLayout( const rendering::StringContext& rOrigContext,
+ const ::cppcanvas::internal::Action::Subset& rSubset,
+ const uno::Reference< rendering::XTextLayout >& rOrigTextLayout )
+ {
+ // create temporary new text layout with subset string
+ // ---------------------------------------------------
+
+ const sal_Int32 nNewStartPos( rOrigContext.StartPosition + ::std::min(
+ rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
+ const sal_Int32 nNewLength( ::std::max(
+ ::std::min(
+ rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
+ rOrigContext.Length ),
+ sal_Int32( 0 ) ) );
+
+ const rendering::StringContext aContext( rOrigContext.Text,
+ nNewStartPos,
+ nNewLength );
+
+ uno::Reference< rendering::XTextLayout > xTextLayout(
+ rOrigTextLayout->getFont()->createTextLayout( aContext,
+ rOrigTextLayout->getMainTextDirection(),
+ 0 ),
+ uno::UNO_QUERY_THROW );
+
+ return xTextLayout;
+ }
+
+ /** Setup subset text layout
+
+ @param io_rTextLayout
+ Must contain original (full set) text layout on input,
+ will contain subsetted text layout (or empty
+ reference, for empty subsets) on output.
+
+ @param io_rRenderState
+ Must contain original render state on input, will
+ contain shifted render state concatenated with
+ rTransformation on output.
+
+ @param rTransformation
+ Additional transformation, to be prepended to render
+ state
+
+ @param rSubset
+ Subset to prepare
+ */
+ void createSubsetLayout( uno::Reference< rendering::XTextLayout >& io_rTextLayout,
+ rendering::RenderState& io_rRenderState,
+ double& o_rMinPos,
+ double& o_rMaxPos,
+ const ::basegfx::B2DHomMatrix& rTransformation,
+ const Action::Subset& rSubset )
+ {
+ ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);
+
+ if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
+ {
+ // empty range, empty layout
+ io_rTextLayout.clear();
+
+ return;
+ }
+
+ ENSURE_OR_THROW( io_rTextLayout.is(),
+ "createSubsetLayout(): Invalid input layout" );
+
+ const rendering::StringContext& rOrigContext( io_rTextLayout->getText() );
+
+ if( rSubset.mnSubsetBegin == 0 &&
+ rSubset.mnSubsetEnd == rOrigContext.Length )
+ {
+ // full range, no need for subsetting
+ return;
+ }
+
+ uno::Reference< rendering::XTextLayout > xTextLayout(
+ createSubsetLayout( rOrigContext, rSubset, io_rTextLayout ) );
+
+ if( xTextLayout.is() )
+ {
+ xTextLayout->applyLogicalAdvancements(
+ calcSubsetOffsets( io_rRenderState,
+ o_rMinPos,
+ o_rMaxPos,
+ io_rTextLayout,
+ rSubset ) );
+ }
+
+ io_rTextLayout = xTextLayout;
+ }
+
+
+ /** Interface for renderEffectText functor below.
+
+ This is interface is used from the renderEffectText()
+ method below, to call the client implementation.
+ */
+ class TextRenderer
+ {
+ public:
+ virtual ~TextRenderer() {}
+
+ /// Render text with given RenderState
+ virtual bool operator()( const rendering::RenderState& rRenderState ) const = 0;
+ };
+
+ /** Render effect text.
+
+ @param rRenderer
+ Functor object, will be called to render the actual
+ part of the text effect (the text itself and the means
+ to render it are unknown to this method)
+ */
+ bool renderEffectText( const TextRenderer& rRenderer,
+ const rendering::RenderState& rRenderState,
+ const rendering::ViewState& /*rViewState*/,
+ const uno::Reference< rendering::XCanvas >& xCanvas,
+ const ::Color& rShadowColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rReliefOffset )
+ {
+ ::Color aEmptyColor( COL_AUTO );
+ uno::Reference<rendering::XColorSpace> xColorSpace(
+ xCanvas->getDevice()->getDeviceColorSpace() );
+
+ // draw shadow text, if enabled
+ if( rShadowColor != aEmptyColor )
+ {
+ rendering::RenderState aShadowState( rRenderState );
+ ::basegfx::B2DHomMatrix aTranslate;
+
+ aTranslate.translate( rShadowOffset.getX(),
+ rShadowOffset.getY() );
+
+ ::canvas::tools::appendToRenderState(aShadowState, aTranslate);
+
+ aShadowState.DeviceColor =
+ ::vcl::unotools::colorToDoubleSequence( rShadowColor,
+ xColorSpace );
+
+ rRenderer( aShadowState );
+ }
+
+ // draw relief text, if enabled
+ if( rReliefColor != aEmptyColor )
+ {
+ rendering::RenderState aReliefState( rRenderState );
+ ::basegfx::B2DHomMatrix aTranslate;
+
+ aTranslate.translate( rReliefOffset.getX(),
+ rReliefOffset.getY() );
+
+ ::canvas::tools::appendToRenderState(aReliefState, aTranslate);
+
+ aReliefState.DeviceColor =
+ ::vcl::unotools::colorToDoubleSequence( rReliefColor,
+ xColorSpace );
+
+ rRenderer( aReliefState );
+ }
+
+ // draw normal text
+ rRenderer( rRenderState );
+
+ return true;
+ }
+
+
+ ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange& rTextBounds,
+ const ::basegfx::B2DRange& rLineBounds,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const rendering::RenderState& rRenderState,
+ const rendering::ViewState& rViewState )
+ {
+ ::basegfx::B2DRange aBounds( rTextBounds );
+
+ // add extends of text lines
+ aBounds.expand( rLineBounds );
+
+ // TODO(Q3): Provide this functionality at the B2DRange
+ ::basegfx::B2DRange aTotalBounds( aBounds );
+ aTotalBounds.expand(
+ ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getX(),
+ aBounds.getMinY() + rReliefOffset.getY(),
+ aBounds.getMaxX() + rReliefOffset.getX(),
+ aBounds.getMaxY() + rReliefOffset.getY() ) );
+ aTotalBounds.expand(
+ ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getX(),
+ aBounds.getMinY() + rShadowOffset.getY(),
+ aBounds.getMaxX() + rShadowOffset.getX(),
+ aBounds.getMaxY() + rShadowOffset.getY() ) );
+
+ return tools::calcDevicePixelBounds( aTotalBounds,
+ rViewState,
+ rRenderState );
+ }
+
+ void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
+ uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
+ const CanvasSharedPtr& rCanvas,
+ const uno::Sequence< double >& rOffsets,
+ const tools::TextLineInfo rLineInfo )
+ {
+ const ::basegfx::B2DPolyPolygon aPoly(
+ textLinesFromLogicalOffsets(
+ rOffsets,
+ rLineInfo ) );
+
+ o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange();
+
+ o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rCanvas->getUNOCanvas()->getDevice(),
+ aPoly );
+ }
+
+ void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
+ uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
+ const CanvasSharedPtr& rCanvas,
+ double nLineWidth,
+ const tools::TextLineInfo rLineInfo )
+ {
+ const ::basegfx::B2DPolyPolygon aPoly(
+ tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
+ rLineInfo ) );
+
+ o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange();
+
+ o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rCanvas->getUNOCanvas()->getDevice(),
+ aPoly );
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ class TextAction : public Action, private ::boost::noncopyable
+ {
+ public:
+ TextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState );
+
+ TextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform );
+
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual sal_Int32 getActionCount() const;
+
+ private:
+ // TODO(P2): This is potentially a real mass object
+ // (every character might be a separate TextAction),
+ // thus, make it as lightweight as possible. For
+ // example, share common RenderState among several
+ // TextActions, maybe using maOffsets for the
+ // translation.
+
+ uno::Reference< rendering::XCanvasFont > mxFont;
+ const rendering::StringContext maStringContext;
+ const CanvasSharedPtr mpCanvas;
+ rendering::RenderState maState;
+ const sal_Int8 maTextDirection;
+ };
+
+ TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState ) :
+ mxFont( rState.xFont ),
+ maStringContext( rString, nStartPos, nLen ),
+ mpCanvas( rCanvas ),
+ maState(),
+ maTextDirection( rState.textDirection )
+ {
+ init( maState, mxFont,
+ rStartPoint,
+ rState, rCanvas );
+
+ ENSURE_OR_THROW( mxFont.is(),
+ "::cppcanvas::internal::TextAction(): Invalid font" );
+ }
+
+ TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform ) :
+ mxFont( rState.xFont ),
+ maStringContext( rString, nStartPos, nLen ),
+ mpCanvas( rCanvas ),
+ maState(),
+ maTextDirection( rState.textDirection )
+ {
+ init( maState, mxFont,
+ rStartPoint,
+ rState, rCanvas, rTextTransform );
+
+ ENSURE_OR_THROW( mxFont.is(),
+ "::cppcanvas::internal::TextAction(): Invalid font" );
+ }
+
+ bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextAction::render()" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
+ mpCanvas->getViewState(), aLocalState, maTextDirection );
+
+ return true;
+ }
+
+ bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& /*rSubset*/ ) const
+ {
+ OSL_ENSURE( false,
+ "TextAction::render(): Subset not supported by this object" );
+
+ // TODO(P1): Retrieve necessary font metric info for
+ // TextAction from XCanvas. Currently, the
+ // TextActionFactory does not generate this object for
+ // _subsettable_ text
+ return render( rTransformation );
+ }
+
+ ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ // create XTextLayout, to have the
+ // XTextLayout::queryTextBounds() method available
+ uno::Reference< rendering::XTextLayout > xTextLayout(
+ mxFont->createTextLayout(
+ maStringContext,
+ maTextDirection,
+ 0 ) );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
+ xTextLayout->queryTextBounds() ),
+ mpCanvas->getViewState(),
+ aLocalState );
+ }
+
+ ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& /*rSubset*/ ) const
+ {
+ OSL_ENSURE( false,
+ "TextAction::getBounds(): Subset not supported by this object" );
+
+ // TODO(P1): Retrieve necessary font metric info for
+ // TextAction from XCanvas. Currently, the
+ // TextActionFactory does not generate this object for
+ // _subsettable_ text
+ return getBounds( rTransformation );
+ }
+
+ sal_Int32 TextAction::getActionCount() const
+ {
+ // TODO(P1): Retrieve necessary font metric info for
+ // TextAction from XCanvas. Currently, the
+ // TextActionFactory does not generate this object for
+ // _subsettable_ text
+ return 1;
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ class EffectTextAction :
+ public Action,
+ public TextRenderer,
+ private ::boost::noncopyable
+ {
+ public:
+ EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState );
+
+ EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform );
+
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual sal_Int32 getActionCount() const;
+
+ private:
+ /// Interface TextRenderer
+ virtual bool operator()( const rendering::RenderState& rRenderState ) const;
+
+ // TODO(P2): This is potentially a real mass object
+ // (every character might be a separate TextAction),
+ // thus, make it as lightweight as possible. For
+ // example, share common RenderState among several
+ // TextActions, maybe using maOffsets for the
+ // translation.
+
+ uno::Reference< rendering::XCanvasFont > mxFont;
+ const rendering::StringContext maStringContext;
+ const CanvasSharedPtr mpCanvas;
+ rendering::RenderState maState;
+ const tools::TextLineInfo maTextLineInfo;
+ ::basegfx::B2DSize maLinesOverallSize;
+ const double mnLineWidth;
+ uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
+ const ::basegfx::B2DSize maReliefOffset;
+ const ::Color maReliefColor;
+ const ::basegfx::B2DSize maShadowOffset;
+ const ::Color maShadowColor;
+ const sal_Int8 maTextDirection;
+ };
+
+ EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState ) :
+ mxFont( rState.xFont ),
+ maStringContext( rText, nStartPos, nLen ),
+ mpCanvas( rCanvas ),
+ maState(),
+ maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
+ maLinesOverallSize(),
+ mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ),
+ mxTextLines(),
+ maReliefOffset( rReliefOffset ),
+ maReliefColor( rReliefColor ),
+ maShadowOffset( rShadowOffset ),
+ maShadowColor( rShadowColor ),
+ maTextDirection( rState.textDirection )
+ {
+ initEffectLinePolyPolygon( maLinesOverallSize,
+ mxTextLines,
+ rCanvas,
+ mnLineWidth,
+ maTextLineInfo );
+
+ init( maState, mxFont,
+ rStartPoint,
+ rState, rCanvas );
+
+ ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
+ "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
+ }
+
+ EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform ) :
+ mxFont( rState.xFont ),
+ maStringContext( rText, nStartPos, nLen ),
+ mpCanvas( rCanvas ),
+ maState(),
+ maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
+ maLinesOverallSize(),
+ mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ),
+ mxTextLines(),
+ maReliefOffset( rReliefOffset ),
+ maReliefColor( rReliefColor ),
+ maShadowOffset( rShadowOffset ),
+ maShadowColor( rShadowColor ),
+ maTextDirection( rState.textDirection )
+ {
+ initEffectLinePolyPolygon( maLinesOverallSize,
+ mxTextLines,
+ rCanvas,
+ mnLineWidth,
+ maTextLineInfo );
+
+ init( maState, mxFont,
+ rStartPoint,
+ rState, rCanvas, rTextTransform );
+
+ ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
+ "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
+ }
+
+ bool EffectTextAction::operator()( const rendering::RenderState& rRenderState ) const
+ {
+ const rendering::ViewState& rViewState( mpCanvas->getViewState() );
+ const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
+
+ rCanvas->fillPolyPolygon( mxTextLines,
+ rViewState,
+ rRenderState );
+
+ rCanvas->drawText( maStringContext, mxFont,
+ rViewState,
+ rRenderState,
+ maTextDirection );
+
+ return true;
+ }
+
+ bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextAction::render()" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return renderEffectText( *this,
+ aLocalState,
+ mpCanvas->getViewState(),
+ mpCanvas->getUNOCanvas(),
+ maShadowColor,
+ maShadowOffset,
+ maReliefColor,
+ maReliefOffset );
+ }
+
+ bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& /*rSubset*/ ) const
+ {
+ OSL_ENSURE( false,
+ "EffectTextAction::render(): Subset not supported by this object" );
+
+ // TODO(P1): Retrieve necessary font metric info for
+ // TextAction from XCanvas. Currently, the
+ // TextActionFactory does not generate this object for
+ // subsettable text
+ return render( rTransformation );
+ }
+
+ ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ // create XTextLayout, to have the
+ // XTextLayout::queryTextBounds() method available
+ uno::Reference< rendering::XTextLayout > xTextLayout(
+ mxFont->createTextLayout(
+ maStringContext,
+ maTextDirection,
+ 0 ) );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
+ xTextLayout->queryTextBounds() ),
+ ::basegfx::B2DRange( 0,0,
+ maLinesOverallSize.getX(),
+ maLinesOverallSize.getY() ),
+ maReliefOffset,
+ maShadowOffset,
+ aLocalState,
+ mpCanvas->getViewState() );
+ }
+
+ ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& /*rSubset*/ ) const
+ {
+ OSL_ENSURE( false,
+ "EffectTextAction::getBounds(): Subset not supported by this object" );
+
+ // TODO(P1): Retrieve necessary font metric info for
+ // TextAction from XCanvas. Currently, the
+ // TextActionFactory does not generate this object for
+ // _subsettable_ text
+ return getBounds( rTransformation );
+ }
+
+ sal_Int32 EffectTextAction::getActionCount() const
+ {
+ // TODO(P1): Retrieve necessary font metric info for
+ // TextAction from XCanvas. Currently, the
+ // TextActionFactory does not generate this object for
+ // subsettable text
+ return 1;
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ class TextArrayAction : public Action, private ::boost::noncopyable
+ {
+ public:
+ TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState );
+
+ TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform );
+
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual sal_Int32 getActionCount() const;
+
+ private:
+ // TODO(P2): This is potentially a real mass object
+ // (every character might be a separate TextAction),
+ // thus, make it as lightweight as possible. For
+ // example, share common RenderState among several
+ // TextActions, maybe using maOffsets for the
+ // translation.
+
+ uno::Reference< rendering::XTextLayout > mxTextLayout;
+ const CanvasSharedPtr mpCanvas;
+ rendering::RenderState maState;
+ };
+
+ TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState ) :
+ mxTextLayout(),
+ mpCanvas( rCanvas ),
+ maState()
+ {
+ initArrayAction( maState,
+ mxTextLayout,
+ rStartPoint,
+ rString,
+ nStartPos,
+ nLen,
+ rOffsets,
+ rCanvas,
+ rState, NULL );
+ }
+
+ TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::rtl::OUString& rString,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform ) :
+ mxTextLayout(),
+ mpCanvas( rCanvas ),
+ maState()
+ {
+ initArrayAction( maState,
+ mxTextLayout,
+ rStartPoint,
+ rString,
+ nStartPos,
+ nLen,
+ rOffsets,
+ rCanvas,
+ rState,
+ &rTextTransform );
+ }
+
+ bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render()" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+#ifdef SPECIAL_DEBUG
+ aLocalState.Clip.clear();
+ aLocalState.DeviceColor =
+ ::vcl::unotools::colorToDoubleSequence( mpCanvas->getUNOCanvas()->getDevice(),
+ ::Color( 0x80FF0000 ) );
+
+ if( maState.Clip.is() )
+ mpCanvas->getUNOCanvas()->drawPolyPolygon( maState.Clip,
+ mpCanvas->getViewState(),
+ aLocalState );
+
+ aLocalState.DeviceColor = maState.DeviceColor;
+#endif
+
+ mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
+ mpCanvas->getViewState(),
+ aLocalState );
+
+ return true;
+ }
+
+ bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render( subset )" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
+
+ double nDummy0, nDummy1;
+ createSubsetLayout( xTextLayout,
+ aLocalState,
+ nDummy0,
+ nDummy1,
+ rTransformation,
+ rSubset );
+
+ if( !xTextLayout.is() )
+ return true; // empty layout, render nothing
+
+ mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
+ mpCanvas->getViewState(),
+ aLocalState );
+
+ return true;
+ }
+
+ ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
+ mxTextLayout->queryTextBounds() ),
+ mpCanvas->getViewState(),
+ aLocalState );
+ }
+
+ ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
+
+ double nDummy0, nDummy1;
+ createSubsetLayout( xTextLayout,
+ aLocalState,
+ nDummy0,
+ nDummy1,
+ rTransformation,
+ rSubset );
+
+ if( !xTextLayout.is() )
+ return ::basegfx::B2DRange(); // empty layout, empty bounds
+
+ return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
+ xTextLayout->queryTextBounds() ),
+ mpCanvas->getViewState(),
+ aLocalState );
+ }
+
+ sal_Int32 TextArrayAction::getActionCount() const
+ {
+ const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
+
+ return rOrigContext.Length;
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ class EffectTextArrayAction :
+ public Action,
+ public TextRenderer,
+ private ::boost::noncopyable
+ {
+ public:
+ EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState );
+ EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform );
+
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual sal_Int32 getActionCount() const;
+
+ private:
+ // TextRenderer interface
+ virtual bool operator()( const rendering::RenderState& rRenderState ) const;
+
+ // TODO(P2): This is potentially a real mass object
+ // (every character might be a separate TextAction),
+ // thus, make it as lightweight as possible. For
+ // example, share common RenderState among several
+ // TextActions, maybe using maOffsets for the
+ // translation.
+
+ uno::Reference< rendering::XTextLayout > mxTextLayout;
+ const CanvasSharedPtr mpCanvas;
+ rendering::RenderState maState;
+ const tools::TextLineInfo maTextLineInfo;
+ ::basegfx::B2DSize maLinesOverallSize;
+ uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
+ const ::basegfx::B2DSize maReliefOffset;
+ const ::Color maReliefColor;
+ const ::basegfx::B2DSize maShadowOffset;
+ const ::Color maShadowColor;
+ };
+
+ EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState ) :
+ mxTextLayout(),
+ mpCanvas( rCanvas ),
+ maState(),
+ maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
+ maLinesOverallSize(),
+ mxTextLines(),
+ maReliefOffset( rReliefOffset ),
+ maReliefColor( rReliefColor ),
+ maShadowOffset( rShadowOffset ),
+ maShadowColor( rShadowColor )
+ {
+ initEffectLinePolyPolygon( maLinesOverallSize,
+ mxTextLines,
+ rCanvas,
+ rOffsets,
+ maTextLineInfo );
+
+ initArrayAction( maState,
+ mxTextLayout,
+ rStartPoint,
+ rText,
+ nStartPos,
+ nLen,
+ rOffsets,
+ rCanvas,
+ rState, NULL );
+ }
+
+ EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::rtl::OUString& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform ) :
+ mxTextLayout(),
+ mpCanvas( rCanvas ),
+ maState(),
+ maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
+ maLinesOverallSize(),
+ mxTextLines(),
+ maReliefOffset( rReliefOffset ),
+ maReliefColor( rReliefColor ),
+ maShadowOffset( rShadowOffset ),
+ maShadowColor( rShadowColor )
+ {
+ initEffectLinePolyPolygon( maLinesOverallSize,
+ mxTextLines,
+ rCanvas,
+ rOffsets,
+ maTextLineInfo );
+
+ initArrayAction( maState,
+ mxTextLayout,
+ rStartPoint,
+ rText,
+ nStartPos,
+ nLen,
+ rOffsets,
+ rCanvas,
+ rState,
+ &rTextTransform );
+ }
+
+ bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState ) const
+ {
+ const rendering::ViewState& rViewState( mpCanvas->getViewState() );
+ const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
+
+ rCanvas->fillPolyPolygon( mxTextLines,
+ rViewState,
+ rRenderState );
+
+ rCanvas->drawTextLayout( mxTextLayout,
+ rViewState,
+ rRenderState );
+
+ return true;
+ }
+
+ bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return renderEffectText( *this,
+ aLocalState,
+ mpCanvas->getViewState(),
+ mpCanvas->getUNOCanvas(),
+ maShadowColor,
+ maShadowOffset,
+ maReliefColor,
+ maReliefOffset );
+ }
+
+ class EffectTextArrayRenderHelper : public TextRenderer
+ {
+ public:
+ EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
+ const uno::Reference< rendering::XTextLayout >& rTextLayout,
+ const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
+ const rendering::ViewState& rViewState ) :
+ mrCanvas( rCanvas ),
+ mrTextLayout( rTextLayout ),
+ mrLinePolygon( rLinePolygon ),
+ mrViewState( rViewState )
+ {
+ }
+
+ // TextRenderer interface
+ virtual bool operator()( const rendering::RenderState& rRenderState ) const
+ {
+ mrCanvas->fillPolyPolygon( mrLinePolygon,
+ mrViewState,
+ rRenderState );
+
+ mrCanvas->drawTextLayout( mrTextLayout,
+ mrViewState,
+ rRenderState );
+
+ return true;
+ }
+
+ private:
+ const uno::Reference< rendering::XCanvas >& mrCanvas;
+ const uno::Reference< rendering::XTextLayout >& mrTextLayout;
+ const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
+ const rendering::ViewState& mrViewState;
+ };
+
+ bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render( subset )" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
+ const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
+
+ double nMinPos(0.0);
+ double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
+
+ createSubsetLayout( xTextLayout,
+ aLocalState,
+ nMinPos,
+ nMaxPos,
+ rTransformation,
+ rSubset );
+
+ if( !xTextLayout.is() )
+ return true; // empty layout, render nothing
+
+
+ // create and setup local line polygon
+ // ===================================
+
+ uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
+ const rendering::ViewState& rViewState( mpCanvas->getViewState() );
+
+ uno::Reference< rendering::XPolyPolygon2D > xTextLines(
+ ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ xCanvas->getDevice(),
+ tools::createTextLinesPolyPolygon(
+ 0.0, nMaxPos - nMinPos,
+ maTextLineInfo ) ) );
+
+
+ // render everything
+ // =================
+
+ return renderEffectText(
+ EffectTextArrayRenderHelper( xCanvas,
+ xTextLayout,
+ xTextLines,
+ rViewState ),
+ aLocalState,
+ rViewState,
+ xCanvas,
+ maShadowColor,
+ maShadowOffset,
+ maReliefColor,
+ maReliefOffset );
+ }
+
+ ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
+ mxTextLayout->queryTextBounds() ),
+ ::basegfx::B2DRange( 0,0,
+ maLinesOverallSize.getX(),
+ maLinesOverallSize.getY() ),
+ maReliefOffset,
+ maShadowOffset,
+ aLocalState,
+ mpCanvas->getViewState() );
+ }
+
+ ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
+ const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
+
+ double nMinPos(0.0);
+ double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
+
+ createSubsetLayout( xTextLayout,
+ aLocalState,
+ nMinPos,
+ nMaxPos,
+ rTransformation,
+ rSubset );
+
+ if( !xTextLayout.is() )
+ return ::basegfx::B2DRange(); // empty layout, empty bounds
+
+
+ // create and setup local line polygon
+ // ===================================
+
+ const ::basegfx::B2DPolyPolygon aPoly(
+ tools::createTextLinesPolyPolygon(
+ 0.0, nMaxPos - nMinPos,
+ maTextLineInfo ) );
+
+ return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
+ xTextLayout->queryTextBounds() ),
+ ::basegfx::tools::getRange( aPoly ),
+ maReliefOffset,
+ maShadowOffset,
+ aLocalState,
+ mpCanvas->getViewState() );
+ }
+
+ sal_Int32 EffectTextArrayAction::getActionCount() const
+ {
+ const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
+
+ return rOrigContext.Length;
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ class OutlineAction :
+ public Action,
+ public TextRenderer,
+ private ::boost::noncopyable
+ {
+ public:
+ OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::basegfx::B2DRectangle& rOutlineBounds,
+ const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
+ const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState );
+ OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::basegfx::B2DRectangle& rOutlineBounds,
+ const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
+ const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform );
+
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
+ virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const;
+
+ virtual sal_Int32 getActionCount() const;
+
+ private:
+ // TextRenderer interface
+ virtual bool operator()( const rendering::RenderState& rRenderState ) const;
+
+ // TODO(P2): This is potentially a real mass object
+ // (every character might be a separate TextAction),
+ // thus, make it as lightweight as possible. For
+ // example, share common RenderState among several
+ // TextActions, maybe using maOffsets for the
+ // translation.
+
+ uno::Reference< rendering::XPolyPolygon2D > mxTextPoly;
+
+ /** This vector denotes the index of the start polygon
+ for the respective glyph sequence.
+
+ To get a polygon index range for a given character
+ index i, take [ maPolygonGlyphMap[i],
+ maPolygonGlyphMap[i+1] ). Note that this is wrong
+ for BiDi
+ */
+ const ::std::vector< sal_Int32 > maPolygonGlyphMap;
+ const uno::Sequence< double > maOffsets;
+ const CanvasSharedPtr mpCanvas;
+ rendering::RenderState maState;
+ double mnOutlineWidth;
+ const uno::Sequence< double > maFillColor;
+ const tools::TextLineInfo maTextLineInfo;
+ ::basegfx::B2DSize maLinesOverallSize;
+ const ::basegfx::B2DRectangle maOutlineBounds;
+ uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
+ const ::basegfx::B2DSize maReliefOffset;
+ const ::Color maReliefColor;
+ const ::basegfx::B2DSize maShadowOffset;
+ const ::Color maShadowColor;
+ };
+
+ double calcOutlineWidth( const OutDevState& rState,
+ VirtualDevice& rVDev )
+ {
+ const ::basegfx::B2DSize aFontSize( 0,
+ rVDev.GetFont().GetHeight() / 64.0 );
+
+ const double nOutlineWidth(
+ (rState.mapModeTransform * aFontSize).getY() );
+
+ return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
+ }
+
+ OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::basegfx::B2DRectangle& rOutlineBounds,
+ const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
+ const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState ) :
+ mxTextPoly( rTextPoly ),
+ maPolygonGlyphMap( rPolygonGlyphMap ),
+ maOffsets( rOffsets ),
+ mpCanvas( rCanvas ),
+ maState(),
+ mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
+ maFillColor(
+ ::vcl::unotools::colorToDoubleSequence(
+ ::Color( COL_WHITE ),
+ rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
+ maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
+ maLinesOverallSize(),
+ maOutlineBounds( rOutlineBounds ),
+ mxTextLines(),
+ maReliefOffset( rReliefOffset ),
+ maReliefColor( rReliefColor ),
+ maShadowOffset( rShadowOffset ),
+ maShadowColor( rShadowColor )
+ {
+ initEffectLinePolyPolygon( maLinesOverallSize,
+ mxTextLines,
+ rCanvas,
+ rOffsets,
+ maTextLineInfo );
+
+ init( maState,
+ rStartPoint,
+ rState,
+ rCanvas );
+ }
+
+ OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const ::basegfx::B2DRectangle& rOutlineBounds,
+ const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
+ const ::std::vector< sal_Int32 >& rPolygonGlyphMap,
+ const uno::Sequence< double >& rOffsets,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const ::basegfx::B2DHomMatrix& rTextTransform ) :
+ mxTextPoly( rTextPoly ),
+ maPolygonGlyphMap( rPolygonGlyphMap ),
+ maOffsets( rOffsets ),
+ mpCanvas( rCanvas ),
+ maState(),
+ mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
+ maFillColor(
+ ::vcl::unotools::colorToDoubleSequence(
+ ::Color( COL_WHITE ),
+ rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
+ maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
+ maLinesOverallSize(),
+ maOutlineBounds( rOutlineBounds ),
+ mxTextLines(),
+ maReliefOffset( rReliefOffset ),
+ maReliefColor( rReliefColor ),
+ maShadowOffset( rShadowOffset ),
+ maShadowColor( rShadowColor )
+ {
+ initEffectLinePolyPolygon( maLinesOverallSize,
+ mxTextLines,
+ rCanvas,
+ rOffsets,
+ maTextLineInfo );
+
+ init( maState,
+ rStartPoint,
+ rState,
+ rCanvas,
+ rTextTransform );
+ }
+
+ bool OutlineAction::operator()( const rendering::RenderState& rRenderState ) const
+ {
+ const rendering::ViewState& rViewState( mpCanvas->getViewState() );
+ const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
+
+ rendering::StrokeAttributes aStrokeAttributes;
+
+ aStrokeAttributes.StrokeWidth = mnOutlineWidth;
+ aStrokeAttributes.MiterLimit = 1.0;
+ aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
+ aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
+ aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
+
+ rendering::RenderState aLocalState( rRenderState );
+ aLocalState.DeviceColor = maFillColor;
+
+ // TODO(P1): implement caching
+
+ // background of text
+ rCanvas->fillPolyPolygon( mxTextPoly,
+ rViewState,
+ aLocalState );
+
+ // border line of text
+ rCanvas->strokePolyPolygon( mxTextPoly,
+ rViewState,
+ rRenderState,
+ aStrokeAttributes );
+
+ // underlines/strikethrough - background
+ rCanvas->fillPolyPolygon( mxTextLines,
+ rViewState,
+ aLocalState );
+ // underlines/strikethrough - border
+ rCanvas->strokePolyPolygon( mxTextLines,
+ rViewState,
+ rRenderState,
+ aStrokeAttributes );
+
+ return true;
+ }
+
+ bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return renderEffectText( *this,
+ aLocalState,
+ mpCanvas->getViewState(),
+ mpCanvas->getUNOCanvas(),
+ maShadowColor,
+ maShadowOffset,
+ maReliefColor,
+ maReliefOffset );
+ }
+
+ class OutlineTextArrayRenderHelper : public TextRenderer
+ {
+ public:
+ OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
+ const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
+ const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
+ const rendering::ViewState& rViewState,
+ double nOutlineWidth ) :
+ maFillColor(
+ ::vcl::unotools::colorToDoubleSequence(
+ ::Color( COL_WHITE ),
+ rCanvas->getDevice()->getDeviceColorSpace() )),
+ mnOutlineWidth( nOutlineWidth ),
+ mrCanvas( rCanvas ),
+ mrTextPolygon( rTextPolygon ),
+ mrLinePolygon( rLinePolygon ),
+ mrViewState( rViewState )
+ {
+ }
+
+ // TextRenderer interface
+ virtual bool operator()( const rendering::RenderState& rRenderState ) const
+ {
+ rendering::StrokeAttributes aStrokeAttributes;
+
+ aStrokeAttributes.StrokeWidth = mnOutlineWidth;
+ aStrokeAttributes.MiterLimit = 1.0;
+ aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
+ aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
+ aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
+
+ rendering::RenderState aLocalState( rRenderState );
+ aLocalState.DeviceColor = maFillColor;
+
+ // TODO(P1): implement caching
+
+ // background of text
+ mrCanvas->fillPolyPolygon( mrTextPolygon,
+ mrViewState,
+ aLocalState );
+
+ // border line of text
+ mrCanvas->strokePolyPolygon( mrTextPolygon,
+ mrViewState,
+ rRenderState,
+ aStrokeAttributes );
+
+ // underlines/strikethrough - background
+ mrCanvas->fillPolyPolygon( mrLinePolygon,
+ mrViewState,
+ aLocalState );
+ // underlines/strikethrough - border
+ mrCanvas->strokePolyPolygon( mrLinePolygon,
+ mrViewState,
+ rRenderState,
+ aStrokeAttributes );
+
+ return true;
+ }
+
+ private:
+ const uno::Sequence< double > maFillColor;
+ double mnOutlineWidth;
+ const uno::Reference< rendering::XCanvas >& mrCanvas;
+ const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon;
+ const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
+ const rendering::ViewState& mrViewState;
+ };
+
+ bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& rSubset ) const
+ {
+ RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::OutlineAction::render( subset )" );
+ RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::OutlineAction: 0x%X", this );
+
+ if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
+ return true; // empty range, render nothing
+
+#if 1
+ // TODO(F3): Subsetting NYI for outline text!
+ return render( rTransformation );
+#else
+ const rendering::StringContext rOrigContext( mxTextLayout->getText() );
+
+ if( rSubset.mnSubsetBegin == 0 &&
+ rSubset.mnSubsetEnd == rOrigContext.Length )
+ {
+ // full range, no need for subsetting
+ return render( rTransformation );
+ }
+
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+
+ // create and setup local Text polygon
+ // ===================================
+
+ uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
+
+ // TODO(P3): Provide an API method for that!
+
+ if( !xTextLayout.is() )
+ return false;
+
+ // render everything
+ // =================
+
+ return renderEffectText(
+ OutlineTextArrayRenderHelper(
+ xCanvas,
+ mnOutlineWidth,
+ xTextLayout,
+ xTextLines,
+ rViewState ),
+ aLocalState,
+ rViewState,
+ xCanvas,
+ maShadowColor,
+ maShadowOffset,
+ maReliefColor,
+ maReliefOffset );
+#endif
+ }
+
+ ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
+ {
+ rendering::RenderState aLocalState( maState );
+ ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
+
+ return calcEffectTextBounds( maOutlineBounds,
+ ::basegfx::B2DRange( 0,0,
+ maLinesOverallSize.getX(),
+ maLinesOverallSize.getY() ),
+ maReliefOffset,
+ maShadowOffset,
+ aLocalState,
+ mpCanvas->getViewState() );
+ }
+
+ ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
+ const Subset& /*rSubset*/ ) const
+ {
+ OSL_ENSURE( false,
+ "OutlineAction::getBounds(): Subset not yet supported by this object" );
+
+ return getBounds( rTransformation );
+ }
+
+ sal_Int32 OutlineAction::getActionCount() const
+ {
+ // TODO(F3): Subsetting NYI for outline text!
+ return maOffsets.getLength();
+ }
+
+
+ // ======================================================================
+ //
+ // Action factory methods
+ //
+ // ======================================================================
+
+ /** Create an outline action
+
+ This method extracts the polygonal outline from the
+ text, and creates a properly setup OutlineAction from
+ it.
+ */
+ ActionSharedPtr createOutline( const ::basegfx::B2DPoint& rStartPoint,
+ const ::basegfx::B2DSize& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::basegfx::B2DSize& rShadowOffset,
+ const ::Color& rShadowColor,
+ const String& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const sal_Int32* pDXArray,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const Renderer::Parameters& rParms )
+ {
+ // operate on raw DX array here (in logical coordinate
+ // system), to have a higher resolution
+ // PolyPolygon. That polygon is then converted to
+ // device coordinate system.
+
+ // #i68512# Temporarily switch off font rotation
+ // (which is already contained in the render state
+ // transformation matrix - otherwise, glyph polygons
+ // will be rotated twice)
+ const ::Font aOrigFont( rVDev.GetFont() );
+ ::Font aUnrotatedFont( aOrigFont );
+ aUnrotatedFont.SetOrientation(0);
+ rVDev.SetFont( aUnrotatedFont );
+
+ // TODO(F3): Don't understand parameter semantics of
+ // GetTextOutlines()
+ ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
+ PolyPolyVector aVCLPolyPolyVector;
+ const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
+ static_cast<USHORT>(nStartPos),
+ static_cast<USHORT>(nStartPos),
+ static_cast<USHORT>(nLen),
+ TRUE, 0, pDXArray ) );
+ rVDev.SetFont(aOrigFont);
+
+ if( !bHaveOutlines )
+ return ActionSharedPtr();
+
+ ::std::vector< sal_Int32 > aPolygonGlyphMap;
+
+ // first glyph starts at polygon index 0
+ aPolygonGlyphMap.push_back( 0 );
+
+ // remove offsetting from mapmode transformation
+ // (outline polygons must stay at origin, only need to
+ // be scaled)
+ ::basegfx::B2DHomMatrix aMapModeTransform(
+ rState.mapModeTransform );
+ aMapModeTransform.set(0,2, 0.0);
+ aMapModeTransform.set(1,2, 0.0);
+
+ PolyPolyVector::const_iterator aIter( aVCLPolyPolyVector.begin() );
+ const PolyPolyVector::const_iterator aEnd( aVCLPolyPolyVector.end() );
+ for( ; aIter!= aEnd; ++aIter )
+ {
+ ::basegfx::B2DPolyPolygon aPolyPolygon;
+
+ aPolyPolygon = aIter->getB2DPolyPolygon();
+ aPolyPolygon.transform( aMapModeTransform );
+
+ // append result to collecting polypoly
+ for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
+ {
+ // #i47795# Ensure closed polygons (since
+ // FreeType returns the glyph outlines
+ // open)
+ const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
+ const sal_uInt32 nCount( rPoly.count() );
+ if( nCount<3 ||
+ rPoly.isClosed() )
+ {
+ // polygon either degenerate, or
+ // already closed.
+ aResultingPolyPolygon.append( rPoly );
+ }
+ else
+ {
+ ::basegfx::B2DPolygon aPoly(rPoly);
+ aPoly.setClosed(true);
+
+ aResultingPolyPolygon.append( aPoly );
+ }
+ }
+
+ // TODO(F3): Depending on the semantics of
+ // GetTextOutlines(), this here is wrong!
+
+ // calc next glyph index
+ aPolygonGlyphMap.push_back( aResultingPolyPolygon.count() );
+ }
+
+ const uno::Sequence< double > aCharWidthSeq(
+ pDXArray ?
+ setupDXArray( pDXArray, nLen, rState ) :
+ setupDXArray( rText,
+ nStartPos,
+ nLen,
+ rVDev,
+ rState ));
+ const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
+ ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ rCanvas->getUNOCanvas()->getDevice(),
+ aResultingPolyPolygon ) );
+
+ if( rParms.maTextTransformation.isValid() )
+ {
+ return ActionSharedPtr(
+ new OutlineAction(
+ rStartPoint,
+ rReliefOffset,
+ rReliefColor,
+ rShadowOffset,
+ rShadowColor,
+ ::basegfx::tools::getRange(aResultingPolyPolygon),
+ xTextPoly,
+ aPolygonGlyphMap,
+ aCharWidthSeq,
+ rVDev,
+ rCanvas,
+ rState,
+ rParms.maTextTransformation.getValue() ) );
+ }
+ else
+ {
+ return ActionSharedPtr(
+ new OutlineAction(
+ rStartPoint,
+ rReliefOffset,
+ rReliefColor,
+ rShadowOffset,
+ rShadowColor,
+ ::basegfx::tools::getRange(aResultingPolyPolygon),
+ xTextPoly,
+ aPolygonGlyphMap,
+ aCharWidthSeq,
+ rVDev,
+ rCanvas,
+ rState ) );
+ }
+ }
+
+ } // namespace
+
+
+ // ---------------------------------------------------------------------------------
+
+ ActionSharedPtr TextActionFactory::createTextAction( const ::Point& rStartPoint,
+ const ::Size& rReliefOffset,
+ const ::Color& rReliefColor,
+ const ::Size& rShadowOffset,
+ const ::Color& rShadowColor,
+ const String& rText,
+ sal_Int32 nStartPos,
+ sal_Int32 nLen,
+ const sal_Int32* pDXArray,
+ VirtualDevice& rVDev,
+ const CanvasSharedPtr& rCanvas,
+ const OutDevState& rState,
+ const Renderer::Parameters& rParms,
+ bool bSubsettable )
+ {
+ const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
+ rVDev ) );
+ // #143885# maintain (nearly) full precision positioning,
+ // by circumventing integer-based OutDev-mapping
+ const ::basegfx::B2DPoint aStartPoint(
+ rState.mapModeTransform *
+ ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
+ rStartPoint.Y() + aBaselineOffset.Height()) );
+
+ const ::basegfx::B2DSize aReliefOffset(
+ rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
+ const ::basegfx::B2DSize aShadowOffset(
+ rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
+
+ if( rState.isTextOutlineModeSet )
+ {
+ return createOutline(
+ aStartPoint,
+ aReliefOffset,
+ rReliefColor,
+ aShadowOffset,
+ rShadowColor,
+ rText,
+ nStartPos,
+ nLen,
+ pDXArray,
+ rVDev,
+ rCanvas,
+ rState,
+ rParms );
+ }
+
+ // convert DX array to device coordinate system (and
+ // create it in the first place, if pDXArray is NULL)
+ const uno::Sequence< double > aCharWidths(
+ pDXArray ?
+ setupDXArray( pDXArray, nLen, rState ) :
+ setupDXArray( rText,
+ nStartPos,
+ nLen,
+ rVDev,
+ rState ));
+
+ // determine type of text action to create
+ // =======================================
+
+ const ::Color aEmptyColor( COL_AUTO );
+
+ // no DX array, and no need to subset - no need to store
+ // DX array, then.
+ if( !pDXArray && !bSubsettable )
+ {
+ // effects, or not?
+ if( !rState.textOverlineStyle &&
+ !rState.textUnderlineStyle &&
+ !rState.textStrikeoutStyle &&
+ rReliefColor == aEmptyColor &&
+ rShadowColor == aEmptyColor )
+ {
+ // nope
+ if( rParms.maTextTransformation.isValid() )
+ {
+ return ActionSharedPtr( new TextAction(
+ aStartPoint,
+ rText,
+ nStartPos,
+ nLen,
+ rCanvas,
+ rState,
+ rParms.maTextTransformation.getValue() ) );
+ }
+ else
+ {
+ return ActionSharedPtr( new TextAction(
+ aStartPoint,
+ rText,
+ nStartPos,
+ nLen,
+ rCanvas,
+ rState ) );
+ }
+ }
+ else
+ {
+ // at least one of the effects requested
+ if( rParms.maTextTransformation.isValid() )
+ return ActionSharedPtr( new EffectTextAction(
+ aStartPoint,
+ aReliefOffset,
+ rReliefColor,
+ aShadowOffset,
+ rShadowColor,
+ rText,
+ nStartPos,
+ nLen,
+ rVDev,
+ rCanvas,
+ rState,
+ rParms.maTextTransformation.getValue() ) );
+ else
+ return ActionSharedPtr( new EffectTextAction(
+ aStartPoint,
+ aReliefOffset,
+ rReliefColor,
+ aShadowOffset,
+ rShadowColor,
+ rText,
+ nStartPos,
+ nLen,
+ rVDev,
+ rCanvas,
+ rState ) );
+ }
+ }
+ else
+ {
+ // DX array necessary - any effects?
+ if( !rState.textOverlineStyle &&
+ !rState.textUnderlineStyle &&
+ !rState.textStrikeoutStyle &&
+ rReliefColor == aEmptyColor &&
+ rShadowColor == aEmptyColor )
+ {
+ // nope
+ if( rParms.maTextTransformation.isValid() )
+ return ActionSharedPtr( new TextArrayAction(
+ aStartPoint,
+ rText,
+ nStartPos,
+ nLen,
+ aCharWidths,
+ rCanvas,
+ rState,
+ rParms.maTextTransformation.getValue() ) );
+ else
+ return ActionSharedPtr( new TextArrayAction(
+ aStartPoint,
+ rText,
+ nStartPos,
+ nLen,
+ aCharWidths,
+ rCanvas,
+ rState ) );
+ }
+ else
+ {
+ // at least one of the effects requested
+ if( rParms.maTextTransformation.isValid() )
+ return ActionSharedPtr( new EffectTextArrayAction(
+ aStartPoint,
+ aReliefOffset,
+ rReliefColor,
+ aShadowOffset,
+ rShadowColor,
+ rText,
+ nStartPos,
+ nLen,
+ aCharWidths,
+ rVDev,
+ rCanvas,
+ rState,
+ rParms.maTextTransformation.getValue() ) );
+ else
+ return ActionSharedPtr( new EffectTextArrayAction(
+ aStartPoint,
+ aReliefOffset,
+ rReliefColor,
+ aShadowOffset,
+ rShadowColor,
+ rText,
+ nStartPos,
+ nLen,
+ aCharWidths,
+ rVDev,
+ rCanvas,
+ rState ) );
+ }
+ }
+#if defined __GNUC__
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 1
+ // Unreachable; to avoid bogus warning:
+ return ActionSharedPtr();
+#endif
+#endif
+ }
+ }
+}