Revert "Clang WaE: -Woverloaded-virtual weirdness, this seems to help"
as that broke tinderboxes. Try another way instead, renaming two
versions of the overloaded render() method, so that there is no longer
any overloading.
Compiles -Werror-clean with Clang, hopefully no problem with other
compilers either.
This reverts commit 86b99ab408
.
2295 lines
116 KiB
C++
2295 lines
116 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* 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.
|
|
*
|
|
************************************************************************/
|
|
|
|
|
|
#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 renderSubset( 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::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& /*rSubset*/ ) const
|
|
{
|
|
OSL_FAIL( "TextAction::renderSubset(): 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_FAIL( "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 renderSubset( 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::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& /*rSubset*/ ) const
|
|
{
|
|
OSL_FAIL( "EffectTextAction::renderSubset(): 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_FAIL( "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 renderSubset( 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);
|
|
|
|
mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
|
|
mpCanvas->getViewState(),
|
|
aLocalState );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const
|
|
{
|
|
RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::renderSubset()" );
|
|
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 renderSubset( 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::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const
|
|
{
|
|
RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
|
|
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 renderSubset( 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::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const
|
|
{
|
|
RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::OutlineAction::renderSubset()" );
|
|
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_FAIL( "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__)
|
|
return ActionSharedPtr();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|