| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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<sal_uInt16>(nStartPos), |
| static_cast<sal_uInt16>(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<sal_uInt16>(rStringContext.StartPosition), |
| static_cast<sal_uInt16>(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<sal_uInt16>(nStartPos), |
| static_cast<sal_uInt16>(nStartPos), |
| static_cast<sal_uInt16>(nLen), |
| sal_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.is_initialized() ) |
| { |
| return ActionSharedPtr( |
| new OutlineAction( |
| rStartPoint, |
| rReliefOffset, |
| rReliefColor, |
| rShadowOffset, |
| rShadowColor, |
| ::basegfx::tools::getRange(aResultingPolyPolygon), |
| xTextPoly, |
| aPolygonGlyphMap, |
| aCharWidthSeq, |
| rVDev, |
| rCanvas, |
| rState, |
| *rParms.maTextTransformation ) ); |
| } |
| 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.is_initialized() ) |
| { |
| return ActionSharedPtr( new TextAction( |
| aStartPoint, |
| rText, |
| nStartPos, |
| nLen, |
| rCanvas, |
| rState, |
| *rParms.maTextTransformation ) ); |
| } |
| else |
| { |
| return ActionSharedPtr( new TextAction( |
| aStartPoint, |
| rText, |
| nStartPos, |
| nLen, |
| rCanvas, |
| rState ) ); |
| } |
| } |
| else |
| { |
| // at least one of the effects requested |
| if( rParms.maTextTransformation.is_initialized() ) |
| return ActionSharedPtr( new EffectTextAction( |
| aStartPoint, |
| aReliefOffset, |
| rReliefColor, |
| aShadowOffset, |
| rShadowColor, |
| rText, |
| nStartPos, |
| nLen, |
| rVDev, |
| rCanvas, |
| rState, |
| *rParms.maTextTransformation ) ); |
| 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.is_initialized() ) |
| return ActionSharedPtr( new TextArrayAction( |
| aStartPoint, |
| rText, |
| nStartPos, |
| nLen, |
| aCharWidths, |
| rCanvas, |
| rState, |
| *rParms.maTextTransformation ) ); |
| else |
| return ActionSharedPtr( new TextArrayAction( |
| aStartPoint, |
| rText, |
| nStartPos, |
| nLen, |
| aCharWidths, |
| rCanvas, |
| rState ) ); |
| } |
| else |
| { |
| // at least one of the effects requested |
| if( rParms.maTextTransformation.is_initialized() ) |
| return ActionSharedPtr( new EffectTextArrayAction( |
| aStartPoint, |
| aReliefOffset, |
| rReliefColor, |
| aShadowOffset, |
| rShadowColor, |
| rText, |
| nStartPos, |
| nLen, |
| aCharWidths, |
| rVDev, |
| rCanvas, |
| rState, |
| *rParms.maTextTransformation ) ); |
| 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 |
| } |
| } |
| } |