2370 lines
123 KiB
C++
2370 lines
123 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
|
|
#include <comphelper/diagnose_ex.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/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 <utility>
|
|
#include <vcl/canvastools.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
|
|
#include <basegfx/utils/canvastools.hxx>
|
|
#include <canvas/canvastools.hxx>
|
|
#include <memory>
|
|
#include <sal/log.hxx>
|
|
|
|
#include "textaction.hxx"
|
|
#include "textlineshelper.hxx"
|
|
#include <outdevstate.hxx>
|
|
#include "mtftools.hxx"
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace cppcanvas::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,
|
|
nullptr,
|
|
&rState.fontRotation );
|
|
|
|
basegfx::B2DHomMatrix aLocalTransformation(basegfx::utils::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 );
|
|
}
|
|
|
|
void initLayoutWidth(double& rLayoutWidth, const uno::Sequence<double>& rOffsets)
|
|
{
|
|
ENSURE_OR_THROW(rOffsets.hasElements(),
|
|
"::cppcanvas::internal::initLayoutWidth(): zero-length array" );
|
|
rLayoutWidth = *(std::max_element(rOffsets.begin(), rOffsets.end()));
|
|
}
|
|
|
|
uno::Sequence< double > setupDXArray( KernArraySpan rCharWidths,
|
|
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++ = rCharWidths[i] * nScale;
|
|
}
|
|
|
|
return aCharWidthSeq;
|
|
}
|
|
|
|
uno::Sequence< double > setupDXArray( const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
VirtualDevice const & rVDev,
|
|
const OutDevState& rState )
|
|
{
|
|
// no external DX array given, create one from given
|
|
// string
|
|
KernArray aCharWidths;
|
|
|
|
rVDev.GetTextArray( rText, &aCharWidths, nStartPos, nLen );
|
|
|
|
return setupDXArray( aCharWidths, 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 OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix* pTextTransform )
|
|
{
|
|
ENSURE_OR_THROW( rOffsets.hasElements(),
|
|
"::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 );
|
|
o_rTextLayout->applyKashidaPositions( rKashidas );
|
|
|
|
}
|
|
|
|
double getLineWidth( ::VirtualDevice const & 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).getWidth();
|
|
}
|
|
|
|
uno::Sequence< double >
|
|
calcSubsetOffsets( rendering::RenderState& io_rRenderState,
|
|
double& o_rMinPos,
|
|
double& o_rMaxPos,
|
|
const uno::Reference< rendering::XTextLayout >& rOrigTextLayout,
|
|
double nLayoutWidth,
|
|
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" );
|
|
|
|
|
|
// 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 )) );
|
|
|
|
// Logical advancements always increase in logical text order.
|
|
// For RTL text, nMaxPos is the distance from the right edge to
|
|
// the leftmost position in the subset, so we have to convert
|
|
// it to the offset from the origin (i.e. left edge ).
|
|
// LTR: |---- min --->|---- max --->| |
|
|
// RTL: | |<--- max ----|<--- min ---|
|
|
// |<- nOffset ->| |
|
|
const double nOffset = rOrigTextLayout->getMainTextDirection()
|
|
? nLayoutWidth - nMaxPos : nMinPos;
|
|
|
|
|
|
// 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 (nOffset > 0.0)
|
|
{
|
|
::basegfx::B2DHomMatrix aTranslation;
|
|
if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical == css::util::TriState_YES )
|
|
{
|
|
// vertical text -> offset in y direction
|
|
aTranslation.translate(0.0, nOffset);
|
|
}
|
|
else
|
|
{
|
|
// horizontal text -> offset in x direction
|
|
aTranslation.translate(nOffset, 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,
|
|
[nMinPos](double aPos) { return aPos - 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_SET_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,
|
|
double nLayoutWidth,
|
|
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 aOrigContext( io_rTextLayout->getText() );
|
|
|
|
if( rSubset.mnSubsetBegin == 0 &&
|
|
rSubset.mnSubsetEnd == aOrigContext.Length )
|
|
{
|
|
// full range, no need for subsetting
|
|
return;
|
|
}
|
|
|
|
uno::Reference< rendering::XTextLayout > xTextLayout(
|
|
createSubsetLayout( aOrigContext, rSubset, io_rTextLayout ) );
|
|
|
|
if( xTextLayout.is() )
|
|
{
|
|
xTextLayout->applyLogicalAdvancements(
|
|
calcSubsetOffsets( io_rRenderState,
|
|
o_rMinPos,
|
|
o_rMaxPos,
|
|
io_rTextLayout,
|
|
nLayoutWidth,
|
|
rSubset ) );
|
|
uno::Sequence< sal_Bool > aOrigKashidaPositions(io_rTextLayout->queryKashidaPositions());
|
|
uno::Sequence< sal_Bool > aKashidaPositions(aOrigKashidaPositions.getArray() + rSubset.mnSubsetBegin,
|
|
rSubset.mnSubsetEnd - rSubset.mnSubsetBegin);
|
|
xTextLayout->applyKashidaPositions(aKashidaPositions);
|
|
}
|
|
|
|
io_rTextLayout = std::move(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 ::Color& rTextFillColor, bool bNormalText ) 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 uno::Reference< rendering::XCanvas >& xCanvas,
|
|
const ::Color& rShadowColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rTextFillColor )
|
|
{
|
|
::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.getWidth(),
|
|
rShadowOffset.getHeight());
|
|
|
|
::canvas::tools::appendToRenderState(aShadowState, aTranslate);
|
|
|
|
aShadowState.DeviceColor =
|
|
vcl::unotools::colorToDoubleSequence( rShadowColor,
|
|
xColorSpace );
|
|
|
|
rRenderer( aShadowState, rTextFillColor, false );
|
|
}
|
|
|
|
// draw relief text, if enabled
|
|
if( rReliefColor != aEmptyColor )
|
|
{
|
|
rendering::RenderState aReliefState( rRenderState );
|
|
::basegfx::B2DHomMatrix aTranslate;
|
|
|
|
aTranslate.translate(rReliefOffset.getWidth(),
|
|
rReliefOffset.getHeight());
|
|
|
|
::canvas::tools::appendToRenderState(aReliefState, aTranslate);
|
|
|
|
aReliefState.DeviceColor =
|
|
vcl::unotools::colorToDoubleSequence( rReliefColor,
|
|
xColorSpace );
|
|
|
|
rRenderer( aReliefState, rTextFillColor, false );
|
|
}
|
|
|
|
// draw normal text
|
|
rRenderer( rRenderState, rTextFillColor, true );
|
|
|
|
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.getWidth(),
|
|
aBounds.getMinY() + rReliefOffset.getHeight(),
|
|
aBounds.getMaxX() + rReliefOffset.getWidth(),
|
|
aBounds.getMaxY() + rReliefOffset.getHeight() ) );
|
|
aTotalBounds.expand(
|
|
::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getWidth(),
|
|
aBounds.getMinY() + rShadowOffset.getHeight(),
|
|
aBounds.getMaxX() + rShadowOffset.getWidth(),
|
|
aBounds.getMaxY() + rShadowOffset.getHeight() ) );
|
|
|
|
return tools::calcDevicePixelBounds( aTotalBounds,
|
|
rViewState,
|
|
rRenderState );
|
|
}
|
|
|
|
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 ) );
|
|
auto aRange = basegfx::utils::getRange( aPoly ).getRange();
|
|
o_rOverallSize = basegfx::B2DSize(aRange.getX(), aRange.getY());
|
|
|
|
o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
rCanvas->getUNOCanvas()->getDevice(),
|
|
aPoly );
|
|
}
|
|
|
|
|
|
class TextAction : public Action
|
|
{
|
|
public:
|
|
TextAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState );
|
|
|
|
TextAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform );
|
|
|
|
TextAction(const TextAction&) = delete;
|
|
const TextAction& operator=(const TextAction&) = delete;
|
|
|
|
virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual sal_Int32 getActionCount() const override;
|
|
|
|
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 OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState ) :
|
|
mxFont( rState.xFont ),
|
|
maStringContext( rString, nStartPos, nLen ),
|
|
mpCanvas( rCanvas ),
|
|
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 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 ),
|
|
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
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction::render()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction: 0x" << std::hex << 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
|
|
{
|
|
SAL_WARN( "cppcanvas.emf", "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
|
|
{
|
|
SAL_WARN( "cppcanvas.emf", "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
|
|
{
|
|
public:
|
|
EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
VirtualDevice const & 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 ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform );
|
|
|
|
EffectTextAction(const EffectTextAction&) = delete;
|
|
const EffectTextAction& operator=(const EffectTextAction&) = delete;
|
|
|
|
virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual sal_Int32 getActionCount() const override;
|
|
|
|
private:
|
|
/// Interface TextRenderer
|
|
virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
|
|
|
|
geometry::RealRectangle2D queryTextBounds() const;
|
|
css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) 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;
|
|
uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
|
|
const ::basegfx::B2DSize maReliefOffset;
|
|
const ::Color maReliefColor;
|
|
const ::basegfx::B2DSize maShadowOffset;
|
|
const ::Color maShadowColor;
|
|
const ::Color maTextFillColor;
|
|
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 ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState ) :
|
|
mxFont( rState.xFont ),
|
|
maStringContext( rText, nStartPos, nLen ),
|
|
mpCanvas( rCanvas ),
|
|
maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
|
|
maReliefOffset( rReliefOffset ),
|
|
maReliefColor( rReliefColor ),
|
|
maShadowOffset( rShadowOffset ),
|
|
maShadowColor( rShadowColor ),
|
|
maTextFillColor( rTextFillColor ),
|
|
maTextDirection( rState.textDirection )
|
|
{
|
|
const double nLineWidth(getLineWidth( rVDev, rState, maStringContext ));
|
|
initEffectLinePolyPolygon( maLinesOverallSize,
|
|
mxTextLines,
|
|
rCanvas,
|
|
nLineWidth,
|
|
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 ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform ) :
|
|
mxFont( rState.xFont ),
|
|
maStringContext( rText, nStartPos, nLen ),
|
|
mpCanvas( rCanvas ),
|
|
maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
|
|
maReliefOffset( rReliefOffset ),
|
|
maReliefColor( rReliefColor ),
|
|
maShadowOffset( rShadowOffset ),
|
|
maShadowColor( rShadowColor ),
|
|
maTextFillColor( rTextFillColor ),
|
|
maTextDirection( rState.textDirection )
|
|
{
|
|
const double nLineWidth( getLineWidth( rVDev, rState, maStringContext ) );
|
|
initEffectLinePolyPolygon( maLinesOverallSize,
|
|
mxTextLines,
|
|
rCanvas,
|
|
nLineWidth,
|
|
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 ::Color& rTextFillColor, bool /*bNormalText*/ ) const
|
|
{
|
|
const rendering::ViewState aViewState( mpCanvas->getViewState() );
|
|
const uno::Reference< rendering::XCanvas > aCanvas( mpCanvas->getUNOCanvas() );
|
|
|
|
//rhbz#1589029 non-transparent text fill background support
|
|
if (rTextFillColor != COL_AUTO)
|
|
{
|
|
rendering::RenderState aLocalState( rRenderState );
|
|
aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
|
|
rTextFillColor, aCanvas->getDevice()->getDeviceColorSpace());
|
|
auto xTextBounds = queryTextBounds(aCanvas);
|
|
// background of text
|
|
aCanvas->fillPolyPolygon(xTextBounds, aViewState, aLocalState);
|
|
}
|
|
|
|
// under/over lines
|
|
aCanvas->fillPolyPolygon( mxTextLines,
|
|
aViewState,
|
|
rRenderState );
|
|
|
|
aCanvas->drawText( maStringContext, mxFont,
|
|
aViewState,
|
|
rRenderState,
|
|
maTextDirection );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction::render()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction: 0x" << std::hex << this );
|
|
|
|
rendering::RenderState aLocalState( maState );
|
|
::canvas::tools::prependToRenderState(aLocalState, rTransformation);
|
|
|
|
return renderEffectText( *this,
|
|
aLocalState,
|
|
mpCanvas->getUNOCanvas(),
|
|
maShadowColor,
|
|
maShadowOffset,
|
|
maReliefColor,
|
|
maReliefOffset,
|
|
maTextFillColor);
|
|
}
|
|
|
|
bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& /*rSubset*/ ) const
|
|
{
|
|
SAL_WARN( "cppcanvas.emf", "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 );
|
|
}
|
|
|
|
geometry::RealRectangle2D EffectTextAction::queryTextBounds() const
|
|
{
|
|
// create XTextLayout, to have the
|
|
// XTextLayout::queryTextBounds() method available
|
|
uno::Reference< rendering::XTextLayout > xTextLayout(
|
|
mxFont->createTextLayout(
|
|
maStringContext,
|
|
maTextDirection,
|
|
0 ) );
|
|
|
|
return xTextLayout->queryTextBounds();
|
|
}
|
|
|
|
css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
|
|
{
|
|
auto aTextBounds = queryTextBounds();
|
|
auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
|
|
auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
|
|
return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
|
|
}
|
|
|
|
::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
|
|
{
|
|
rendering::RenderState aLocalState( maState );
|
|
::canvas::tools::prependToRenderState(aLocalState, rTransformation);
|
|
|
|
return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
|
|
queryTextBounds() ),
|
|
::basegfx::B2DRange( 0,0,
|
|
maLinesOverallSize.getWidth(),
|
|
maLinesOverallSize.getHeight() ),
|
|
maReliefOffset,
|
|
maShadowOffset,
|
|
aLocalState,
|
|
mpCanvas->getViewState() );
|
|
}
|
|
|
|
::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& /*rSubset*/ ) const
|
|
{
|
|
SAL_WARN( "cppcanvas.emf", "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
|
|
{
|
|
public:
|
|
TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState );
|
|
|
|
TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform );
|
|
|
|
TextArrayAction(const TextArrayAction&) = delete;
|
|
const TextArrayAction& operator=(const TextArrayAction&) = delete;
|
|
|
|
virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual sal_Int32 getActionCount() const override;
|
|
|
|
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;
|
|
double mnLayoutWidth;
|
|
};
|
|
|
|
TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState ) :
|
|
mpCanvas( rCanvas )
|
|
{
|
|
initLayoutWidth(mnLayoutWidth, rOffsets);
|
|
|
|
initArrayAction( maState,
|
|
mxTextLayout,
|
|
rStartPoint,
|
|
rString,
|
|
nStartPos,
|
|
nLen,
|
|
rOffsets,
|
|
rKashidas,
|
|
rCanvas,
|
|
rState, nullptr );
|
|
}
|
|
|
|
TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const OUString& rString,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform ) :
|
|
mpCanvas( rCanvas )
|
|
{
|
|
initLayoutWidth(mnLayoutWidth, rOffsets);
|
|
|
|
initArrayAction( maState,
|
|
mxTextLayout,
|
|
rStartPoint,
|
|
rString,
|
|
nStartPos,
|
|
nLen,
|
|
rOffsets,
|
|
rKashidas,
|
|
rCanvas,
|
|
rState,
|
|
&rTextTransform );
|
|
}
|
|
|
|
bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::render()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << 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
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::renderSubset()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
|
|
|
|
rendering::RenderState aLocalState( maState );
|
|
uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
|
|
|
|
double nDummy0, nDummy1;
|
|
createSubsetLayout( xTextLayout,
|
|
mnLayoutWidth,
|
|
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
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
|
|
|
|
rendering::RenderState aLocalState( maState );
|
|
uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
|
|
|
|
double nDummy0, nDummy1;
|
|
createSubsetLayout( xTextLayout,
|
|
mnLayoutWidth,
|
|
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 aOrigContext( mxTextLayout->getText() );
|
|
|
|
return aOrigContext.Length;
|
|
}
|
|
|
|
|
|
class EffectTextArrayAction :
|
|
public Action,
|
|
public TextRenderer
|
|
{
|
|
public:
|
|
EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
VirtualDevice const & 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 ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform );
|
|
|
|
EffectTextArrayAction(const EffectTextArrayAction&) = delete;
|
|
const EffectTextArrayAction& operator=(const EffectTextArrayAction&) = delete;
|
|
|
|
virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual sal_Int32 getActionCount() const override;
|
|
|
|
private:
|
|
// TextRenderer interface
|
|
virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
|
|
|
|
css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) 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;
|
|
TextLinesHelper maTextLinesHelper;
|
|
const ::basegfx::B2DSize maReliefOffset;
|
|
const ::Color maReliefColor;
|
|
const ::basegfx::B2DSize maShadowOffset;
|
|
const ::Color maShadowColor;
|
|
const ::Color maTextFillColor;
|
|
double mnLayoutWidth;
|
|
};
|
|
|
|
EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState ) :
|
|
mpCanvas( rCanvas ),
|
|
maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
|
|
maTextLinesHelper(mpCanvas, rState),
|
|
maReliefOffset( rReliefOffset ),
|
|
maReliefColor( rReliefColor ),
|
|
maShadowOffset( rShadowOffset ),
|
|
maShadowColor( rShadowColor ),
|
|
maTextFillColor( rTextFillColor )
|
|
{
|
|
initLayoutWidth(mnLayoutWidth, rOffsets);
|
|
|
|
maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
|
|
|
|
initArrayAction( maState,
|
|
mxTextLayout,
|
|
rStartPoint,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
rOffsets,
|
|
rKashidas,
|
|
rCanvas,
|
|
rState, nullptr );
|
|
}
|
|
|
|
EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
const uno::Sequence< double >& rOffsets,
|
|
const uno::Sequence< sal_Bool >& rKashidas,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform ) :
|
|
mpCanvas( rCanvas ),
|
|
maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
|
|
maTextLinesHelper(mpCanvas, rState),
|
|
maReliefOffset( rReliefOffset ),
|
|
maReliefColor( rReliefColor ),
|
|
maShadowOffset( rShadowOffset ),
|
|
maShadowColor( rShadowColor ),
|
|
maTextFillColor( rTextFillColor )
|
|
{
|
|
initLayoutWidth(mnLayoutWidth, rOffsets);
|
|
|
|
maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
|
|
|
|
initArrayAction( maState,
|
|
mxTextLayout,
|
|
rStartPoint,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
rOffsets,
|
|
rKashidas,
|
|
rCanvas,
|
|
rState,
|
|
&rTextTransform );
|
|
}
|
|
|
|
css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextArrayAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
|
|
{
|
|
const geometry::RealRectangle2D aTextBounds(mxTextLayout->queryTextBounds());
|
|
auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
|
|
auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
|
|
return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
|
|
}
|
|
|
|
bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText) const
|
|
{
|
|
const rendering::ViewState aViewState( mpCanvas->getViewState() );
|
|
const uno::Reference< rendering::XCanvas > aCanvas( mpCanvas->getUNOCanvas() );
|
|
|
|
//rhbz#1589029 non-transparent text fill background support
|
|
if (rTextFillColor != COL_AUTO)
|
|
{
|
|
rendering::RenderState aLocalState(rRenderState);
|
|
aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
|
|
rTextFillColor, aCanvas->getDevice()->getDeviceColorSpace());
|
|
auto xTextBounds = queryTextBounds(aCanvas);
|
|
// background of text
|
|
aCanvas->fillPolyPolygon(xTextBounds, aViewState, aLocalState);
|
|
}
|
|
|
|
// under/over lines
|
|
maTextLinesHelper.render(rRenderState, bNormalText);
|
|
|
|
aCanvas->drawTextLayout( mxTextLayout,
|
|
aViewState,
|
|
rRenderState );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
|
|
|
|
rendering::RenderState aLocalState( maState );
|
|
::canvas::tools::prependToRenderState(aLocalState, rTransformation);
|
|
|
|
return renderEffectText( *this,
|
|
aLocalState,
|
|
mpCanvas->getUNOCanvas(),
|
|
maShadowColor,
|
|
maShadowOffset,
|
|
maReliefColor,
|
|
maReliefOffset,
|
|
maTextFillColor);
|
|
}
|
|
|
|
class EffectTextArrayRenderHelper : public TextRenderer
|
|
{
|
|
public:
|
|
EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
|
|
const uno::Reference< rendering::XTextLayout >& rTextLayout,
|
|
const TextLinesHelper& rTextLinesHelper,
|
|
const rendering::ViewState& rViewState ) :
|
|
mrCanvas( rCanvas ),
|
|
mrTextLayout( rTextLayout ),
|
|
mrTextLinesHelper( rTextLinesHelper ),
|
|
mrViewState( rViewState )
|
|
{
|
|
}
|
|
|
|
// TextRenderer interface
|
|
virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor,bool bNormalText) const override
|
|
{
|
|
mrTextLinesHelper.render(rRenderState, bNormalText);
|
|
|
|
//rhbz#1589029 non-transparent text fill background support
|
|
if (rTextFillColor != COL_AUTO)
|
|
{
|
|
rendering::RenderState aLocalState(rRenderState);
|
|
aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
|
|
rTextFillColor, mrCanvas->getDevice()->getDeviceColorSpace());
|
|
auto xTextBounds = queryTextBounds();
|
|
// background of text
|
|
mrCanvas->fillPolyPolygon(xTextBounds, mrViewState, aLocalState);
|
|
}
|
|
|
|
mrCanvas->drawTextLayout( mrTextLayout,
|
|
mrViewState,
|
|
rRenderState );
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds() const
|
|
{
|
|
const geometry::RealRectangle2D aTextBounds(mrTextLayout->queryTextBounds());
|
|
auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
|
|
auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
|
|
return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(mrCanvas->getDevice(), aTextBoundsPoly);
|
|
}
|
|
|
|
const uno::Reference< rendering::XCanvas >& mrCanvas;
|
|
const uno::Reference< rendering::XTextLayout >& mrTextLayout;
|
|
const TextLinesHelper& mrTextLinesHelper;
|
|
const rendering::ViewState& mrViewState;
|
|
};
|
|
|
|
bool EffectTextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << 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,
|
|
mnLayoutWidth,
|
|
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 aViewState( mpCanvas->getViewState() );
|
|
|
|
TextLinesHelper aHelper = maTextLinesHelper;
|
|
aHelper.init(nMaxPos - nMinPos, maTextLineInfo);
|
|
|
|
|
|
// render everything
|
|
// =================
|
|
|
|
return renderEffectText(
|
|
EffectTextArrayRenderHelper( xCanvas,
|
|
xTextLayout,
|
|
aHelper,
|
|
aViewState ),
|
|
aLocalState,
|
|
xCanvas,
|
|
maShadowColor,
|
|
maShadowOffset,
|
|
maReliefColor,
|
|
maReliefOffset,
|
|
maTextFillColor);
|
|
}
|
|
|
|
::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
|
|
{
|
|
rendering::RenderState aLocalState( maState );
|
|
::canvas::tools::prependToRenderState(aLocalState, rTransformation);
|
|
|
|
::basegfx::B2DSize aSize = maTextLinesHelper.getOverallSize();
|
|
|
|
return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
|
|
mxTextLayout->queryTextBounds() ),
|
|
basegfx::B2DRange(0, 0,
|
|
aSize.getWidth(),
|
|
aSize.getHeight()),
|
|
maReliefOffset,
|
|
maShadowOffset,
|
|
aLocalState,
|
|
mpCanvas->getViewState() );
|
|
}
|
|
|
|
::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << 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,
|
|
mnLayoutWidth,
|
|
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::utils::getRange( aPoly ),
|
|
maReliefOffset,
|
|
maShadowOffset,
|
|
aLocalState,
|
|
mpCanvas->getViewState() );
|
|
}
|
|
|
|
sal_Int32 EffectTextArrayAction::getActionCount() const
|
|
{
|
|
const rendering::StringContext aOrigContext( mxTextLayout->getText() );
|
|
|
|
return aOrigContext.Length;
|
|
}
|
|
|
|
|
|
class OutlineAction :
|
|
public Action,
|
|
public TextRenderer
|
|
{
|
|
public:
|
|
OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rFillColor,
|
|
uno::Reference< rendering::XPolyPolygon2D > xFillPoly,
|
|
const ::basegfx::B2DRectangle& rOutlineBounds,
|
|
uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
|
|
const uno::Sequence< double >& rOffsets,
|
|
VirtualDevice const & 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 ::Color& rFillColor,
|
|
uno::Reference< rendering::XPolyPolygon2D > xFillPoly,
|
|
const ::basegfx::B2DRectangle& rOutlineBounds,
|
|
uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
|
|
const uno::Sequence< double >& rOffsets,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform );
|
|
|
|
OutlineAction(const OutlineAction&) = delete;
|
|
const OutlineAction& operator=(const OutlineAction&) = delete;
|
|
|
|
virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
|
|
virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const override;
|
|
|
|
virtual sal_Int32 getActionCount() const override;
|
|
|
|
private:
|
|
// TextRenderer interface
|
|
virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
|
|
|
|
// 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;
|
|
|
|
const uno::Sequence< double > maOffsets;
|
|
const CanvasSharedPtr mpCanvas;
|
|
rendering::RenderState maState;
|
|
double mnOutlineWidth;
|
|
const uno::Sequence< double > maFillColor;
|
|
uno::Reference< rendering::XPolyPolygon2D > mxBackgroundFillPoly;
|
|
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;
|
|
const ::Color maTextFillColor;
|
|
const ::Color maBackgroundFillColor;
|
|
};
|
|
|
|
double calcOutlineWidth( const OutDevState& rState,
|
|
VirtualDevice const & rVDev )
|
|
{
|
|
const ::basegfx::B2DSize aFontSize( 0,
|
|
rVDev.GetFont().GetFontHeight() / 64.0 );
|
|
|
|
const double nOutlineWidth(
|
|
(rState.mapModeTransform * aFontSize).getHeight() );
|
|
|
|
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 ::Color& rFillColor,
|
|
uno::Reference< rendering::XPolyPolygon2D > xFillPoly,
|
|
const ::basegfx::B2DRectangle& rOutlineBounds,
|
|
uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
|
|
const uno::Sequence< double >& rOffsets,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState ) :
|
|
mxTextPoly(std::move( xTextPoly )),
|
|
maOffsets( rOffsets ),
|
|
mpCanvas( rCanvas ),
|
|
mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
|
|
maFillColor(
|
|
vcl::unotools::colorToDoubleSequence(
|
|
COL_WHITE,
|
|
rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
|
|
mxBackgroundFillPoly(std::move( xFillPoly )),
|
|
maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
|
|
maOutlineBounds( rOutlineBounds ),
|
|
maReliefOffset( rReliefOffset ),
|
|
maReliefColor( rReliefColor ),
|
|
maShadowOffset( rShadowOffset ),
|
|
maShadowColor( rShadowColor ),
|
|
maBackgroundFillColor( rFillColor )
|
|
{
|
|
double nLayoutWidth = 0.0;
|
|
|
|
initLayoutWidth(nLayoutWidth, rOffsets);
|
|
|
|
initEffectLinePolyPolygon( maLinesOverallSize,
|
|
mxTextLines,
|
|
rCanvas,
|
|
nLayoutWidth,
|
|
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 ::Color& rFillColor,
|
|
uno::Reference< rendering::XPolyPolygon2D > xFillPoly,
|
|
const ::basegfx::B2DRectangle& rOutlineBounds,
|
|
uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
|
|
const uno::Sequence< double >& rOffsets,
|
|
VirtualDevice const & rVDev,
|
|
const CanvasSharedPtr& rCanvas,
|
|
const OutDevState& rState,
|
|
const ::basegfx::B2DHomMatrix& rTextTransform ) :
|
|
mxTextPoly(std::move( xTextPoly )),
|
|
maOffsets( rOffsets ),
|
|
mpCanvas( rCanvas ),
|
|
mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
|
|
maFillColor(
|
|
vcl::unotools::colorToDoubleSequence(
|
|
COL_WHITE,
|
|
rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
|
|
mxBackgroundFillPoly(std::move( xFillPoly )),
|
|
maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
|
|
maOutlineBounds( rOutlineBounds ),
|
|
maReliefOffset( rReliefOffset ),
|
|
maReliefColor( rReliefColor ),
|
|
maShadowOffset( rShadowOffset ),
|
|
maShadowColor( rShadowColor ),
|
|
maBackgroundFillColor( rFillColor )
|
|
{
|
|
double nLayoutWidth = 0.0;
|
|
initLayoutWidth(nLayoutWidth, rOffsets);
|
|
|
|
initEffectLinePolyPolygon( maLinesOverallSize,
|
|
mxTextLines,
|
|
rCanvas,
|
|
nLayoutWidth,
|
|
maTextLineInfo );
|
|
|
|
init( maState,
|
|
rStartPoint,
|
|
rState,
|
|
rCanvas,
|
|
rTextTransform );
|
|
}
|
|
|
|
bool OutlineAction::operator()( const rendering::RenderState& rRenderState, const ::Color& /*rTextFillColor*/, bool /*bNormalText*/ ) const
|
|
{
|
|
const rendering::ViewState aViewState( mpCanvas->getViewState() );
|
|
const uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
|
|
|
|
if (mxBackgroundFillPoly.is())
|
|
{
|
|
rendering::RenderState aLocalState( rRenderState );
|
|
aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
|
|
maBackgroundFillColor, xCanvas->getDevice()->getDeviceColorSpace());
|
|
xCanvas->fillPolyPolygon(mxBackgroundFillPoly, aViewState, aLocalState);
|
|
}
|
|
|
|
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
|
|
xCanvas->fillPolyPolygon( mxTextPoly,
|
|
aViewState,
|
|
aLocalState );
|
|
|
|
// border line of text
|
|
xCanvas->strokePolyPolygon( mxTextPoly,
|
|
aViewState,
|
|
rRenderState,
|
|
aStrokeAttributes );
|
|
|
|
// underlines/strikethrough - background
|
|
xCanvas->fillPolyPolygon( mxTextLines,
|
|
aViewState,
|
|
aLocalState );
|
|
// underlines/strikethrough - border
|
|
xCanvas->strokePolyPolygon( mxTextLines,
|
|
aViewState,
|
|
rRenderState,
|
|
aStrokeAttributes );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
|
|
|
|
rendering::RenderState aLocalState( maState );
|
|
::canvas::tools::prependToRenderState(aLocalState, rTransformation);
|
|
|
|
return renderEffectText( *this,
|
|
aLocalState,
|
|
mpCanvas->getUNOCanvas(),
|
|
maShadowColor,
|
|
maShadowOffset,
|
|
maReliefColor,
|
|
maReliefOffset,
|
|
maTextFillColor);
|
|
}
|
|
|
|
#if 0 // see #if'ed out use in OutlineAction::renderSubset below:
|
|
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(
|
|
::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;
|
|
};
|
|
#endif
|
|
|
|
bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& rSubset ) const
|
|
{
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction::renderSubset()" );
|
|
SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction: 0x" << std::hex << 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.getWidth(),
|
|
maLinesOverallSize.getHeight()),
|
|
maReliefOffset,
|
|
maShadowOffset,
|
|
aLocalState,
|
|
mpCanvas->getViewState() );
|
|
}
|
|
|
|
::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
|
|
const Subset& /*rSubset*/ ) const
|
|
{
|
|
SAL_WARN( "cppcanvas.emf", "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.
|
|
*/
|
|
std::shared_ptr<Action> createOutline( const ::basegfx::B2DPoint& rStartPoint,
|
|
const ::basegfx::B2DSize& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::basegfx::B2DSize& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
KernArraySpan pDXArray,
|
|
std::span<const sal_Bool> pKashidaArray,
|
|
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 vcl::Font aOrigFont( rVDev.GetFont() );
|
|
vcl::Font aUnrotatedFont( aOrigFont );
|
|
aUnrotatedFont.SetOrientation(0_deg10);
|
|
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),
|
|
0, pDXArray, pKashidaArray ) );
|
|
rVDev.SetFont(aOrigFont);
|
|
|
|
if( !bHaveOutlines )
|
|
return std::shared_ptr<Action>();
|
|
|
|
// 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);
|
|
|
|
for( const auto& rVCLPolyPolygon : aVCLPolyPolyVector )
|
|
{
|
|
::basegfx::B2DPolyPolygon aPolyPolygon = rVCLPolyPolygon.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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
const uno::Sequence< double > aCharWidthSeq(
|
|
!pDXArray.empty() ?
|
|
setupDXArray( pDXArray, nLen, rState ) :
|
|
setupDXArray( rText,
|
|
nStartPos,
|
|
nLen,
|
|
rVDev,
|
|
rState ));
|
|
const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
|
|
::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
rCanvas->getUNOCanvas()->getDevice(),
|
|
aResultingPolyPolygon ) );
|
|
|
|
// create background color fill polygon?
|
|
css::uno::Reference<css::rendering::XPolyPolygon2D> xTextBoundsPoly;
|
|
if (rTextFillColor != COL_AUTO)
|
|
{
|
|
rendering::StringContext aStringContext( rText, nStartPos, nLen );
|
|
uno::Reference< rendering::XTextLayout > xTextLayout(
|
|
rState.xFont->createTextLayout(
|
|
aStringContext,
|
|
rState.textDirection,
|
|
0 ) );
|
|
|
|
auto aTextBounds = xTextLayout->queryTextBounds();
|
|
auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
|
|
auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
|
|
xTextBoundsPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolygon(
|
|
rCanvas->getUNOCanvas()->getDevice(),
|
|
aTextBoundsPoly);
|
|
}
|
|
|
|
if( rParms.maTextTransformation )
|
|
{
|
|
return std::make_shared<OutlineAction>(
|
|
rStartPoint,
|
|
rReliefOffset,
|
|
rReliefColor,
|
|
rShadowOffset,
|
|
rShadowColor,
|
|
rTextFillColor,
|
|
xTextBoundsPoly,
|
|
::basegfx::utils::getRange(aResultingPolyPolygon),
|
|
xTextPoly,
|
|
aCharWidthSeq,
|
|
rVDev,
|
|
rCanvas,
|
|
rState,
|
|
*rParms.maTextTransformation );
|
|
}
|
|
else
|
|
{
|
|
return std::make_shared<OutlineAction>(
|
|
rStartPoint,
|
|
rReliefOffset,
|
|
rReliefColor,
|
|
rShadowOffset,
|
|
rShadowColor,
|
|
rTextFillColor,
|
|
xTextBoundsPoly,
|
|
::basegfx::utils::getRange(aResultingPolyPolygon),
|
|
xTextPoly,
|
|
aCharWidthSeq,
|
|
rVDev,
|
|
rCanvas,
|
|
rState );
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
std::shared_ptr<Action> TextActionFactory::createTextAction( const ::Point& rStartPoint,
|
|
const ::Size& rReliefOffset,
|
|
const ::Color& rReliefColor,
|
|
const ::Size& rShadowOffset,
|
|
const ::Color& rShadowColor,
|
|
const ::Color& rTextFillColor,
|
|
const OUString& rText,
|
|
sal_Int32 nStartPos,
|
|
sal_Int32 nLen,
|
|
KernArraySpan pDXArray,
|
|
std::span<const sal_Bool> pKashidaArray,
|
|
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,
|
|
rTextFillColor,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
pDXArray,
|
|
pKashidaArray,
|
|
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.empty() ?
|
|
setupDXArray( pDXArray, nLen, rState ) :
|
|
setupDXArray( rText,
|
|
nStartPos,
|
|
nLen,
|
|
rVDev,
|
|
rState ));
|
|
|
|
const uno::Sequence< sal_Bool > aKashidas(pKashidaArray.data(), pKashidaArray.size());
|
|
|
|
// determine type of text action to create
|
|
// =======================================
|
|
|
|
const ::Color aEmptyColor( COL_AUTO );
|
|
|
|
std::shared_ptr<Action> ret;
|
|
|
|
// no DX array, and no need to subset - no need to store
|
|
// DX array, then.
|
|
if( pDXArray.empty() && !bSubsettable )
|
|
{
|
|
// effects, or not?
|
|
if( !rState.textOverlineStyle &&
|
|
!rState.textUnderlineStyle &&
|
|
!rState.textStrikeoutStyle &&
|
|
rReliefColor == aEmptyColor &&
|
|
rShadowColor == aEmptyColor &&
|
|
rTextFillColor == aEmptyColor )
|
|
{
|
|
// nope
|
|
if( rParms.maTextTransformation )
|
|
{
|
|
ret = std::make_shared<TextAction>(
|
|
aStartPoint,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
rCanvas,
|
|
rState,
|
|
*rParms.maTextTransformation );
|
|
}
|
|
else
|
|
{
|
|
ret = std::make_shared<TextAction>(
|
|
aStartPoint,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
rCanvas,
|
|
rState );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// at least one of the effects requested
|
|
if( rParms.maTextTransformation )
|
|
ret = std::make_shared<EffectTextAction>(
|
|
aStartPoint,
|
|
aReliefOffset,
|
|
rReliefColor,
|
|
aShadowOffset,
|
|
rShadowColor,
|
|
rTextFillColor,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
rVDev,
|
|
rCanvas,
|
|
rState,
|
|
*rParms.maTextTransformation );
|
|
else
|
|
ret = std::make_shared<EffectTextAction>(
|
|
aStartPoint,
|
|
aReliefOffset,
|
|
rReliefColor,
|
|
aShadowOffset,
|
|
rShadowColor,
|
|
rTextFillColor,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
rVDev,
|
|
rCanvas,
|
|
rState );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DX array necessary - any effects?
|
|
if( !rState.textOverlineStyle &&
|
|
!rState.textUnderlineStyle &&
|
|
!rState.textStrikeoutStyle &&
|
|
rReliefColor == aEmptyColor &&
|
|
rShadowColor == aEmptyColor &&
|
|
rTextFillColor == aEmptyColor )
|
|
{
|
|
// nope
|
|
if( rParms.maTextTransformation )
|
|
ret = std::make_shared<TextArrayAction>(
|
|
aStartPoint,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
aCharWidths,
|
|
aKashidas,
|
|
rCanvas,
|
|
rState,
|
|
*rParms.maTextTransformation );
|
|
else
|
|
ret = std::make_shared<TextArrayAction>(
|
|
aStartPoint,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
aCharWidths,
|
|
aKashidas,
|
|
rCanvas,
|
|
rState );
|
|
}
|
|
else
|
|
{
|
|
// at least one of the effects requested
|
|
if( rParms.maTextTransformation )
|
|
ret = std::make_shared<EffectTextArrayAction>(
|
|
aStartPoint,
|
|
aReliefOffset,
|
|
rReliefColor,
|
|
aShadowOffset,
|
|
rShadowColor,
|
|
rTextFillColor,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
aCharWidths,
|
|
aKashidas,
|
|
rVDev,
|
|
rCanvas,
|
|
rState,
|
|
*rParms.maTextTransformation );
|
|
else
|
|
ret = std::make_shared<EffectTextArrayAction>(
|
|
aStartPoint,
|
|
aReliefOffset,
|
|
rReliefColor,
|
|
aShadowOffset,
|
|
rShadowColor,
|
|
rTextFillColor,
|
|
rText,
|
|
nStartPos,
|
|
nLen,
|
|
aCharWidths,
|
|
aKashidas,
|
|
rVDev,
|
|
rCanvas,
|
|
rState );
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|