diff options
Diffstat (limited to 'drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx')
-rw-r--r-- | drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 2030 |
1 files changed, 2030 insertions, 0 deletions
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx new file mode 100644 index 000000000000..263cdeede4f8 --- /dev/null +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -0,0 +1,2030 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_drawinglayer.hxx" + +#include <drawinglayer/processor2d/vclmetafileprocessor2d.hxx> +#include <tools/gen.hxx> +#include <vcl/virdev.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/gradient.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/processor2d/vclpixelprocessor2d.hxx> +#include <tools/stream.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <vcl/graphictools.hxx> +#include <vcl/metaact.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <comphelper/processfactory.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/i18n/CharacterIteratorMode.hdl> +#include <com/sun/star/i18n/WordType.hpp> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> +#include <helperchartrenderer.hxx> +#include <drawinglayer/primitive2d/epsprimitive2d.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for PDFExtOutDevData Graphic support + +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/formpdfexport.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for Control printing + +#include <com/sun/star/beans/XPropertySet.hpp> + +////////////////////////////////////////////////////////////////////////////// +// for current chart PrettyPrinting support + +#include <drawinglayer/primitive2d/chartprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for StructureTagPrimitive support in sd's unomodel.cxx + +#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// +// #112245# definition for maximum allowed point count due to Metafile target. +// To be on the safe side with the old tools polygon, use slightly less then +// the theoretical maximum (bad experiences with tools polygon) + +#define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0) + +////////////////////////////////////////////////////////////////////////////// + +namespace +{ + // #112245# helper to split line polygon in half + void splitLinePolygon( + const basegfx::B2DPolygon& rBasePolygon, + basegfx::B2DPolygon& o_aLeft, + basegfx::B2DPolygon& o_aRight) + { + const sal_uInt32 nCount(rBasePolygon.count()); + + if(nCount) + { + const sal_uInt32 nHalfCount((nCount - 1) >> 1); + + o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1); + o_aLeft.setClosed(false); + + o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount); + o_aRight.setClosed(false); + + if(rBasePolygon.isClosed()) + { + o_aRight.append(rBasePolygon.getB2DPoint(0)); + + if(rBasePolygon.areControlPointsUsed()) + { + o_aRight.setControlPoints( + o_aRight.count() - 1, + rBasePolygon.getPrevControlPoint(0), + rBasePolygon.getNextControlPoint(0)); + } + } + } + else + { + o_aLeft.clear(); + o_aRight.clear(); + } + } + + // #112245# helper to evtl. split filled polygons to maximum metafile point count + bool fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon) + { + bool bRetval(false); + const sal_uInt32 nPolyCount(rPolyPolygon.count()); + + if(nPolyCount) + { + basegfx::B2DPolyPolygon aSplitted; + + for(sal_uInt32 a(0); a < nPolyCount; a++) + { + const basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a)); + const sal_uInt32 nPointCount(aCandidate.count()); + bool bNeedToSplit(false); + + if(aCandidate.areControlPointsUsed()) + { + // compare with the maximum for bezier curved polygons + bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1L); + } + else + { + // compare with the maximum for simple point polygons + bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1); + } + + if(bNeedToSplit) + { + // need to split the partial polygon + const basegfx::B2DRange aRange(aCandidate.getB2DRange()); + const basegfx::B2DPoint aCenter(aRange.getCenter()); + + if(aRange.getWidth() > aRange.getHeight()) + { + // clip in left and right + const basegfx::B2DPolyPolygon aLeft( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + false, + true, + aCenter.getX(), + false)); + const basegfx::B2DPolyPolygon aRight( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + false, + false, + aCenter.getX(), + false)); + + aSplitted.append(aLeft); + aSplitted.append(aRight); + } + else + { + // clip in top and bottom + const basegfx::B2DPolyPolygon aTop( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + true, + true, + aCenter.getY(), + false)); + const basegfx::B2DPolyPolygon aBottom( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + true, + false, + aCenter.getY(), + false)); + + aSplitted.append(aTop); + aSplitted.append(aBottom); + } + } + else + { + aSplitted.append(aCandidate); + } + } + + if(aSplitted.count() != nPolyCount) + { + rPolyPolygon = aSplitted; + } + } + + return bRetval; + } +} // end of anonymous namespace + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + Rectangle VclMetafileProcessor2D::impDumpToMetaFile( + const primitive2d::Primitive2DSequence& rContent, + GDIMetaFile& o_rContentMetafile) + { + // Prepare VDev, MetaFile and connections + OutputDevice* pLastOutputDevice = mpOutputDevice; + GDIMetaFile* pLastMetafile = mpMetaFile; + basegfx::B2DRange aPrimitiveRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D())); + + // transform primitive range with current transformation (e.g shadow offset) + aPrimitiveRange.transform(maCurrentTransformation); + + const Rectangle aPrimitiveRectangle( + basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()), + basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY())); + VirtualDevice aContentVDev; + MapMode aNewMapMode(pLastOutputDevice->GetMapMode()); + + mpOutputDevice = &aContentVDev; + mpMetaFile = &o_rContentMetafile; + aContentVDev.EnableOutput(false); + aContentVDev.SetMapMode(pLastOutputDevice->GetMapMode()); + o_rContentMetafile.Record(&aContentVDev); + aContentVDev.SetLineColor(pLastOutputDevice->GetLineColor()); + aContentVDev.SetFillColor(pLastOutputDevice->GetFillColor()); + aContentVDev.SetFont(pLastOutputDevice->GetFont()); + aContentVDev.SetDrawMode(pLastOutputDevice->GetDrawMode()); + aContentVDev.SetSettings(pLastOutputDevice->GetSettings()); + aContentVDev.SetRefPoint(pLastOutputDevice->GetRefPoint()); + + // dump to MetaFile + process(rContent); + + // cleanups + o_rContentMetafile.Stop(); + o_rContentMetafile.WindStart(); + aNewMapMode.SetOrigin(aPrimitiveRectangle.TopLeft()); + o_rContentMetafile.SetPrefMapMode(aNewMapMode); + o_rContentMetafile.SetPrefSize(aPrimitiveRectangle.GetSize()); + mpOutputDevice = pLastOutputDevice; + mpMetaFile = pLastMetafile; + + return aPrimitiveRectangle; + } + + void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient( + Gradient& o_rVCLGradient, + const attribute::FillGradientAttribute& rFiGrAtt, + bool bIsTransparenceGradient) + { + if(bIsTransparenceGradient) + { + // it's about transparence channel intensities (black/white), do not use color modifier + o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor())); + o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor())); + } + else + { + // use color modifier to influence start/end color of gradient + o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor()))); + o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor()))); + } + + o_rVCLGradient.SetAngle(static_cast< sal_uInt16 >(rFiGrAtt.getAngle() * (1.0 / F_PI1800))); + o_rVCLGradient.SetBorder(static_cast< sal_uInt16 >(rFiGrAtt.getBorder() * 100.0)); + o_rVCLGradient.SetOfsX(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetX() * 100.0)); + o_rVCLGradient.SetOfsY(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetY() * 100.0)); + o_rVCLGradient.SetSteps(rFiGrAtt.getSteps()); + + // defaults for intensity; those were computed into the start/end colors already + o_rVCLGradient.SetStartIntensity(100); + o_rVCLGradient.SetEndIntensity(100); + + switch(rFiGrAtt.getStyle()) + { + default : // attribute::GRADIENTSTYLE_LINEAR : + { + o_rVCLGradient.SetStyle(GRADIENT_LINEAR); + break; + } + case attribute::GRADIENTSTYLE_AXIAL : + { + o_rVCLGradient.SetStyle(GRADIENT_AXIAL); + break; + } + case attribute::GRADIENTSTYLE_RADIAL : + { + o_rVCLGradient.SetStyle(GRADIENT_RADIAL); + break; + } + case attribute::GRADIENTSTYLE_ELLIPTICAL : + { + o_rVCLGradient.SetStyle(GRADIENT_ELLIPTICAL); + break; + } + case attribute::GRADIENTSTYLE_SQUARE : + { + o_rVCLGradient.SetStyle(GRADIENT_SQUARE); + break; + } + case attribute::GRADIENTSTYLE_RECT : + { + o_rVCLGradient.SetStyle(GRADIENT_RECT); + break; + } + } + } + + void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill) + { + if(pSvtGraphicFill && !mnSvtGraphicFillCount) + { + SvMemoryStream aMemStm; + + aMemStm << *pSvtGraphicFill; + mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const BYTE* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END))); + mnSvtGraphicFillCount++; + } + } + + void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill) + { + if(pSvtGraphicFill && mnSvtGraphicFillCount) + { + mnSvtGraphicFillCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END")); + delete pSvtGraphicFill; + } + } + + SvtGraphicStroke* VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke( + const basegfx::B2DPolygon& rB2DPolygon, + const basegfx::BColor* pColor, + const attribute::LineAttribute* pLineAttribute, + const attribute::StrokeAttribute* pStrokeAttribute, + const attribute::LineStartEndAttribute* pStart, + const attribute::LineStartEndAttribute* pEnd) + { + SvtGraphicStroke* pRetval = 0; + + if(rB2DPolygon.count() && !mnSvtGraphicStrokeCount) + { + basegfx::BColor aStrokeColor; + basegfx::B2DPolyPolygon aStartArrow; + basegfx::B2DPolyPolygon aEndArrow; + + if(pColor) + { + aStrokeColor = *pColor; + } + else if(pLineAttribute) + { + aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor()); + } + + // It IS needed to record the stroke color at all in the metafile, + // SvtGraphicStroke has NO entry for stroke color(!) + mpOutputDevice->SetLineColor(Color(aStrokeColor)); + + if(!rB2DPolygon.isClosed()) + { + double fPolyLength(0.0); + + if(pStart && pStart->isActive()) + { + fPolyLength = basegfx::tools::getLength(rB2DPolygon); + + aStartArrow = basegfx::tools::createAreaGeometryForLineStartEnd( + rB2DPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), + fPolyLength, pStart->isCentered() ? 0.5 : 0.0, 0); + } + + if(pEnd && pEnd->isActive()) + { + if(basegfx::fTools::equalZero(fPolyLength)) + { + fPolyLength = basegfx::tools::getLength(rB2DPolygon); + } + + aEndArrow = basegfx::tools::createAreaGeometryForLineStartEnd( + rB2DPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), + fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, 0); + } + } + + SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone); + double fLineWidth(0.0); + double fMiterLength(0.0); + SvtGraphicStroke::DashArray aDashArray; + + if(pLineAttribute) + { + // pre-fill fLineWidth + fLineWidth = pLineAttribute->getWidth(); + + // pre-fill fMiterLength + fMiterLength = fLineWidth; + + // get Join + switch(pLineAttribute->getLineJoin()) + { + default : // basegfx::B2DLINEJOIN_NONE : + { + eJoin = SvtGraphicStroke::joinNone; + break; + } + case basegfx::B2DLINEJOIN_BEVEL : + { + eJoin = SvtGraphicStroke::joinBevel; + break; + } + case basegfx::B2DLINEJOIN_MIDDLE : + case basegfx::B2DLINEJOIN_MITER : + { + eJoin = SvtGraphicStroke::joinMiter; + // ATM 15 degrees is assumed + fMiterLength /= rtl::math::sin(M_PI * (15.0 / 360.0)); + break; + } + case basegfx::B2DLINEJOIN_ROUND : + { + eJoin = SvtGraphicStroke::joinRound; + break; + } + } + } + + if(pStrokeAttribute) + { + // copy dash array + aDashArray = pStrokeAttribute->getDotDashArray(); + } + + // #i101734# apply current object transformation to created geometry. + // This is a partial fix. When a object transformation is used which + // e.g. contains a scaleX != scaleY, an unproportional scaling would + // have to be applied to the evtl. existing fat line. The current + // concept of PDF export and SvtGraphicStroke usage does simply not + // allow handling such definitions. The only clean way would be to + // add the transformation to SvtGraphicStroke and to handle it there + basegfx::B2DPolygon aB2DPolygon(rB2DPolygon); + + aB2DPolygon.transform(maCurrentTransformation); + aStartArrow.transform(maCurrentTransformation); + aEndArrow.transform(maCurrentTransformation); + + pRetval = new SvtGraphicStroke( + Polygon(aB2DPolygon), + PolyPolygon(aStartArrow), + PolyPolygon(aEndArrow), + mfCurrentUnifiedTransparence, + fLineWidth, + SvtGraphicStroke::capButt, + eJoin, + fMiterLength, + aDashArray); + } + + return pRetval; + } + + void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke) + { + if(pSvtGraphicStroke && !mnSvtGraphicStrokeCount) + { + SvMemoryStream aMemStm; + + aMemStm << *pSvtGraphicStroke; + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const BYTE* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END))); + mnSvtGraphicStrokeCount++; + } + } + + void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke) + { + if(pSvtGraphicStroke && mnSvtGraphicStrokeCount) + { + mnSvtGraphicStrokeCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END")); + delete pSvtGraphicStroke; + } + } + + // init static break iterator + uno::Reference< ::com::sun::star::i18n::XBreakIterator > VclMetafileProcessor2D::mxBreakIterator; + + VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev) + : VclProcessor2D(rViewInformation, rOutDev), + mpMetaFile(rOutDev.GetConnectMetaFile()), + mnSvtGraphicFillCount(0), + mnSvtGraphicStrokeCount(0), + mfCurrentUnifiedTransparence(0.0), + mpPDFExtOutDevData(dynamic_cast< vcl::PDFExtOutDevData* >(rOutDev.GetExtOutDevData())) + { + OSL_ENSURE(rOutDev.GetConnectMetaFile(), "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)"); + // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation + // but only to ObjectTransformation. Do not change MapMode of destination. + maCurrentTransformation = rViewInformation.getObjectTransformation(); + } + + VclMetafileProcessor2D::~VclMetafileProcessor2D() + { + // MapMode was not changed, no restore necessary + } + + /*********************************************************************************************** + + Support of MetaCommentActions in the VclMetafileProcessor2D + Found MetaCommentActions and how they are supported: + + XGRAD_SEQ_BEGIN, XGRAD_SEQ_END: + + Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action. + It is used in various exporters/importers to have direct access to the gradient before it + is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g. + the Metafile to SdrObject import creates it's gradient objects. + Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D, + map it back to the corresponding tools PolyPolygon and the Gradient and just call + OutputDevice::DrawGradient which creates the necessary compatible actions. + + XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END: + + Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed + inside GDIMetaFile::Rotate, nothing to take care of here. + The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used + with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not + XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it + to the comment action. A closing end token is created in the destructor. + Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and + SdrRectObj. + The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind + of filled objects, even simple colored polygons. It is added as extra information; the + Metafile actions between the two tokens are interpreted as output generated from those + fills. Thus, users have the choice to use the SvtGraphicFill info or the created output + actions. + Even for XFillTransparenceItem it is used, thus it may need to be supported in + UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon. + Implemented for: + PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D, + PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D, + PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D, + PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D, + and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence + + XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END: + + Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one + is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the + contained path accordingly. + The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and + only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this + would hinder to make use of PolyPolygon strokes. I will need to add support at: + PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D + PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D + PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D + This can be done hierarchical, too. + Okay, base implementation done based on those three primitives. + + FIELD_SEQ_BEGIN, FIELD_SEQ_END + + Used from slideshow for URLs, created from diverse SvxField implementations inside + createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx + inside ImpEditEngine::Paint. + Created TextHierarchyFieldPrimitive2D and added needed infos there; it is an group primitive and wraps + text primitives (but is not limited to that). It contains the field type if special actions for the + support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is + needed, it may be supported there. + FIELD_SEQ_BEGIN;PageField + FIELD_SEQ_END + Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too. + + XTEXT + + XTEXT_EOC(i) end of character + XTEXT_EOW(i) end of word + XTEXT_EOS(i) end of sentence + + this three are with index and are created with the help of a i18n::XBreakIterator in + ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some + data structure for holding those TEXT infos. + Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text + primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage + that this creations do not need to be done for all paints all the time. This would be + expensive since the BreakIterator and it's usage is expensive and for each paint also the + whole character stops would need to be created. + Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below) + + XTEXT_EOL() end of line + XTEXT_EOP() end of paragraph + + First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well, + i decided to solve it with structure. I added the TextHierarchyPrimitives for this, + namely: + - TextHierarchyLinePrimitive2D: Encapsulates single line + - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph + - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM) + Those are now supported in hierarchy. This means the MetaFile renderer will support them + by using them, reculrively using their content and adding MetaFile comments as needed. + This also means that when another text layouter will be used it will be necessary to + create/support the same HierarchyPrimitives to support users. + To transport the information using this hierarchy is best suited to all future needs; + the slideshow will be able to profit from it directly when using primitives; all other + renderers not interested in the text structure will just ignore the encapsulations. + + XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END + Supported now by the TextHierarchyBlockPrimitive2D. + + EPSReplacementGraphic: + Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to + hold the original EPS which was imported in the same MetaFile as first 2 entries. Only + used to export the original again (if exists). + Not necessary to support with MetaFuleRenderer. + + XTEXT_SCROLLRECT, XTEXT_PAINTRECT + Currently used to get extra MetaFile infos using GraphicExporter which again uses + SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since + the rectangle data is added directly by the GraphicsExporter as comment. Does not need + to be adapted at once. + When adapting later, the only user - the diashow - should directly use the provided + Anination infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D) + + PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END + VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as + a fix (hack) while VCL printing. It is needed to not downscale a bitmap which + was explicitely created for the printer already again to some default maximum + bitmap sizes. + Nothing to do here for the primitive renderer. + + Support for vcl::PDFExtOutDevData: + PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at + the OutDev. When set, some extra data is written there. Trying simple PDF export and + watching if i get those infos. + Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses + the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check + if i get a PDFExtOutDevData at the target output device. + Indeed, i get one. Checking what all may be done when that extra-device-info is there. + + All in all i have to talk to SJ. I will need to emulate some of those actions, but + i need to discuss which ones. + In the future, all those infos would be taken from the primitive sequence anyways, + thus these extensions would potentially be temporary, too. + Discussed with SJ, added the necessary support and tested it. Details follow. + + - In ImpEditEngine::Paint, paragraph infos and URL stuff is added. + Added in primitive MetaFile renderer. + Checking URL: Indeed, current version exports it, but it is missing in primitive + CWS version. Adding support. + Okay, URLs work. Checked, Done. + + - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the + target and uno control data is created in UnoControlPDFExportContact::do_PaintObject. + This may be added in primitive MetaFile renderer. + Adding support... + OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace + svxform. Have to talk to FS if this has to be like that. Especially since + ::vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl. + Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move + that stuff to somewhere else, maybe tools or svtools ?!? We will see... + Moved to toolkit, so i have to link against it. I tried VCL first, but it did + not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other then the name + may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself, + the lowest move,ment plave is toolkit. + Checked form control export, it works well. Done. + + - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are + generated. I will need to check what happens here with primitives. + To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed. + Added support, but feature is broken in main version, so i cannot test at all. + Writing a bug to CL (or SJ) and seeing what happens (#i80380#). + SJ took a look and we got it working. Tested VCL MetaFile Renderer based export, + as intended, the original file is exported. Works, Done. + + + + + To be done: + + - Maybe there are more places to take care of for vcl::PDFExtOutDevData! + + + + ****************************************************************************************************/ + + void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + { + // directdraw of wrong spell primitive + // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only + break; + } + case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D : + { + const primitive2d::GraphicPrimitive2D& rGraphicPrimitive = static_cast< const primitive2d::GraphicPrimitive2D& >(rCandidate); + bool bUsingPDFExtOutDevData(false); + basegfx::B2DVector aTranslate, aScale; + static bool bSuppressPDFExtOutDevDataSupport(false); + + if(mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport) + { + // emulate data handling from UnoControlPDFExportContact, original see + // svtools/source/graphic/grfmgr.cxx + const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic(); + + if(rGraphic.IsLink()) + { + const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr(); + + if(!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted()) + { + const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform(); + double fRotate, fShearX; + rTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + if( basegfx::fTools::equalZero( fRotate ) && ( aScale.getX() > 0.0 ) && ( aScale.getY() > 0.0 ) ) + { + bUsingPDFExtOutDevData = true; + mpPDFExtOutDevData->BeginGroup(); + } + } + } + } + + // process recursively and add MetaFile comment + process(rGraphicPrimitive.get2DDecomposition(getViewInformation2D())); + + if(bUsingPDFExtOutDevData) + { + // emulate data handling from UnoControlPDFExportContact, original see + // svtools/source/graphic/grfmgr.cxx + const basegfx::B2DRange aCurrentRange( + aTranslate.getX(), aTranslate.getY(), + aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY()); + const Rectangle aCurrentRect( + sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())), + sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY()))); + const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr(); + Rectangle aCropRect; + + if(rAttr.IsCropped()) + { + // calculate scalings between real image size and logic object size. This + // is necessary since the crop values are relative to original bitmap size + double fFactorX(1.0); + double fFactorY(1.0); + + { + const MapMode aMapMode100thmm(MAP_100TH_MM); + const Size aBitmapSize(Application::GetDefaultDevice()->LogicToLogic( + rGraphicPrimitive.getGraphicObject().GetPrefSize(), + rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm)); + const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop()); + const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop()); + + if(!basegfx::fTools::equalZero(fDivX)) + { + fFactorX = aScale.getX() / fDivX; + } + + if(!basegfx::fTools::equalZero(fDivY)) + { + fFactorY = aScale.getY() / fDivY; + } + } + + // calculate crop range and rect + basegfx::B2DRange aCropRange; + aCropRange.expand(aCurrentRange.getMinimum() - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY)); + aCropRange.expand(aCurrentRange.getMaximum() + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY)); + + aCropRect = Rectangle( + sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())), + sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY()))); + } + + mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(), + rAttr.GetTransparency(), + aCurrentRect, + aCropRect); + } + + break; + } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : + { + const primitive2d::ControlPrimitive2D& rControlPrimitive = static_cast< const primitive2d::ControlPrimitive2D& >(rCandidate); + const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl()); + bool bIsPrintableControl(false); + + // find out if control is printable + if(rXControl.is()) + { + try + { + uno::Reference< beans::XPropertySet > xModelProperties(rXControl->getModel(), uno::UNO_QUERY); + uno::Reference< beans::XPropertySetInfo > xPropertyInfo(xModelProperties.is() + ? xModelProperties->getPropertySetInfo() + : uno::Reference< beans::XPropertySetInfo >()); + const ::rtl::OUString sPrintablePropertyName(RTL_CONSTASCII_USTRINGPARAM("Printable")); + + if(xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName)) + { + OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) >>= bIsPrintableControl); + } + } + catch(const uno::Exception&) + { + OSL_ENSURE(false, "VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!"); + } + } + + // PDF export and printing only for printable controls + if(bIsPrintableControl) + { + const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); + bool bDoProcessRecursively(true); + + if(bPDFExport) + { + // PDF export. Emulate data handling from UnoControlPDFExportContact + // I have now moved describePDFControl to toolkit, thus i can implement the PDF + // form control support now as follows + ::std::auto_ptr< ::vcl::PDFWriter::AnyWidget > pPDFControl; + ::toolkitform::describePDFControl(rXControl, pPDFControl); + + if(pPDFControl.get()) + { + // still need to fill in the location (is a class Rectangle) + const basegfx::B2DRange aRangeLogic(rControlPrimitive.getB2DRange(getViewInformation2D())); + const Rectangle aRectLogic( + (sal_Int32)floor(aRangeLogic.getMinX()), (sal_Int32)floor(aRangeLogic.getMinY()), + (sal_Int32)ceil(aRangeLogic.getMaxX()), (sal_Int32)ceil(aRangeLogic.getMaxY())); + pPDFControl->Location = aRectLogic; + + Size aFontSize(pPDFControl->TextFont.GetSize()); + aFontSize = mpOutputDevice->LogicToLogic(aFontSize, MapMode(MAP_POINT), mpOutputDevice->GetMapMode()); + pPDFControl->TextFont.SetSize(aFontSize); + + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form); + mpPDFExtOutDevData->CreateControl(*pPDFControl.get()); + mpPDFExtOutDevData->EndStructureElement(); + + // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject); + // do not process recursively + bDoProcessRecursively = false; + } + else + { + // PDF export did not work, try simple output. + // Fallback to printer output by not setting bDoProcessRecursively + // to false. + } + } + + // #i93169# used flag the wrong way; true means that nothing was done yet + if(bDoProcessRecursively) + { + // printer output + try + { + // remember old graphics and create new + uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics()); + const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics()); + + if(xNewGraphics.is()) + { + // link graphics and view + xControlView->setGraphics(xNewGraphics); + + // get position + const basegfx::B2DHomMatrix aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete * basegfx::B2DPoint(0.0, 0.0)); + + // draw it + xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()), basegfx::fround(aTopLeftDiscrete.getY())); + bDoProcessRecursively = false; + + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + } + } + catch( const uno::Exception& ) + { + OSL_ENSURE(false, "VclMetafileProcessor2D: Printing of Control failed, caught an exception!"); + } + } + + // process recursively if not done yet to export as decomposition (bitmap) + if(bDoProcessRecursively) + { + process(rControlPrimitive.get2DDecomposition(getViewInformation2D())); + } + } + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D : + { + // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to) + // thus do the MetafileAction embedding stuff but just handle recursively. + const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive = static_cast< const primitive2d::TextHierarchyFieldPrimitive2D& >(rCandidate); + static const ByteString aCommentStringCommon("FIELD_SEQ_BEGIN"); + static const ByteString aCommentStringPage("FIELD_SEQ_BEGIN;PageField"); + static const ByteString aCommentStringEnd("FIELD_SEQ_END"); + + switch(rFieldPrimitive.getType()) + { + default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON : + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon)); + break; + } + case drawinglayer::primitive2d::FIELD_TYPE_PAGE : + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage)); + break; + } + case drawinglayer::primitive2d::FIELD_TYPE_URL : + { + const rtl::OUString& rURL = rFieldPrimitive.getString(); + const String aOldString(rURL); + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const BYTE* >(aOldString.GetBuffer()), 2 * aOldString.Len())); + break; + } + } + + // process recursively + const primitive2d::Primitive2DSequence rContent = rFieldPrimitive.get2DDecomposition(getViewInformation2D()); + process(rContent); + + // for the end comment the type is not relevant yet, they are all the same. Just add. + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd)); + + if(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType()) + { + // emulate data handling from ImpEditEngine::Paint + const basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D())); + const Rectangle aRectLogic( + (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()), + (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY())); + vcl::PDFExtOutDevBookmarkEntry aBookmark; + aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic); + aBookmark.aBookmark = rFieldPrimitive.getString(); + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks(); + rBookmarks.push_back( aBookmark ); + } + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D : + { + const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive = static_cast< const primitive2d::TextHierarchyLinePrimitive2D& >(rCandidate); + static const ByteString aCommentString("XTEXT_EOL"); + + // process recursively and add MetaFile comment + process(rLinePrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D : + { + // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The + // "XTEXT_EOC" is used, use here, too. + const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive = static_cast< const primitive2d::TextHierarchyBulletPrimitive2D& >(rCandidate); + static const ByteString aCommentString("XTEXT_EOC"); + + // process recursively and add MetaFile comment + process(rBulletPrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D : + { + const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive = static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D& >(rCandidate); + static const ByteString aCommentString("XTEXT_EOP"); + + if(mpPDFExtOutDevData) + { + // emulate data handling from ImpEditEngine::Paint + mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph ); + } + + // process recursively and add MetaFile comment + process(rParagraphPrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + if(mpPDFExtOutDevData) + { + // emulate data handling from ImpEditEngine::Paint + mpPDFExtOutDevData->EndStructureElement(); + } + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D : + { + const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive = static_cast< const primitive2d::TextHierarchyBlockPrimitive2D& >(rCandidate); + static const ByteString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN"); + static const ByteString aCommentStringB("XTEXT_PAINTSHAPE_END"); + + // add MetaFile comment, process recursively and add MetaFile comment + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA)); + process(rBlockPrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB)); + + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : + { + // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate = static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate); + // const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate); + + // Adapt evtl. used special DrawMode + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); + + // directdraw of text simple portion; use default processing + RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate); + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); + + // #i101169# if(pTextDecoratedCandidate) + { + // support for TEXT_ MetaFile actions only for decorated texts + if(!mxBreakIterator.is()) + { + uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); + mxBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), uno::UNO_QUERY); + } + + if(mxBreakIterator.is()) + { + const rtl::OUString& rTxt = rTextCandidate.getText(); + const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength()); + + if(nTextLength) + { + const ::com::sun::star::lang::Locale& rLocale = rTextCandidate.getLocale(); + const sal_Int32 nTextPosition(rTextCandidate.getTextPosition()); + + sal_Int32 nDone; + sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); + ::com::sun::star::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); + sal_Int32 nNextSentenceBreak(mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); + static const ByteString aCommentStringA("XTEXT_EOC"); + static const ByteString aCommentStringB("XTEXT_EOW"); + static const ByteString aCommentStringC("XTEXT_EOS"); + + for(sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++) + { + // create the entries for the respective break positions + if(i == nNextCellBreak) + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA, i - nTextPosition)); + nNextCellBreak = mxBreakIterator->nextCharacters(rTxt, i, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); + } + if(i == nNextWordBoundary.endPos) + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB, i - nTextPosition)); + nNextWordBoundary = mxBreakIterator->getWordBoundary(rTxt, i + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True); + } + if(i == nNextSentenceBreak) + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringC, i - nTextPosition)); + nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale); + } + } + } + } + } + + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate); + const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon(); + + if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more, split the polygon in half and call recursively + basegfx::B2DPolygon aLeft, aRight; + splitLinePolygon(rBasePolygon, aLeft, aRight); + const primitive2d::PolygonHairlinePrimitive2D aPLeft(aLeft, rHairlinePrimitive.getBColor()); + const primitive2d::PolygonHairlinePrimitive2D aPRight(aRight, rHairlinePrimitive.getBColor()); + + processBasePrimitive2D(aPLeft); + processBasePrimitive2D(aPRight); + } + else + { + // direct draw of hairline; use default processing + // support SvtGraphicStroke MetaCommentAction + const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor())); + SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rHairlinePrimitive.getB2DPolygon(), + &aLineColor, + 0, 0, 0, 0); + + impStartSvtGraphicStroke(pSvtGraphicStroke); + RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), false); + impEndSvtGraphicStroke(pSvtGraphicStroke); + } + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D : + { + const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive = static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate); + const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon(); + + if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more, split the polygon in half and call recursively + basegfx::B2DPolygon aLeft, aRight; + splitLinePolygon(rBasePolygon, aLeft, aRight); + const primitive2d::PolygonStrokePrimitive2D aPLeft( + aLeft, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute()); + const primitive2d::PolygonStrokePrimitive2D aPRight( + aRight, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute()); + + processBasePrimitive2D(aPLeft); + processBasePrimitive2D(aPRight); + } + else + { + // support SvtGraphicStroke MetaCommentAction + SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rBasePolygon, 0, + &rStrokePrimitive.getLineAttribute(), + &rStrokePrimitive.getStrokeAttribute(), + 0, 0); + + impStartSvtGraphicStroke(pSvtGraphicStroke); + const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute(); + + // create MetaPolyLineActions, but without LINE_DASH + if(basegfx::fTools::more(rLine.getWidth(), 0.0)) + { + const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute(); + basegfx::B2DPolyPolygon aHairLinePolyPolygon; + + if(0.0 == rStroke.getFullDotDashLen()) + { + aHairLinePolyPolygon.append(rBasePolygon); + } + else + { + basegfx::tools::applyLineDashing( + rBasePolygon, rStroke.getDotDashArray(), + &aHairLinePolyPolygon, 0, rStroke.getFullDotDashLen()); + } + + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor())); + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + aHairLinePolyPolygon.transform(maCurrentTransformation); + + // #i113922# LineWidth needs to be transformed, too + const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(rLine.getWidth(), 0.0)); + const double fDiscreteLineWidth(aDiscreteUnit.getLength()); + + LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fDiscreteLineWidth)); + aLineInfo.SetLineJoin(rLine.getLineJoin()); + + for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) + { + const basegfx::B2DPolygon aCandidate(aHairLinePolyPolygon.getB2DPolygon(a)); + + if(aCandidate.count() > 1) + { + const Polygon aToolsPolygon(aCandidate); + + mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo)); + } + } + } + else + { + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + + impEndSvtGraphicStroke(pSvtGraphicStroke); + } + + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D : + { + const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive = static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D& >(rCandidate); + const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon(); + + if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more, split the polygon in half and call recursively + basegfx::B2DPolygon aLeft, aRight; + splitLinePolygon(rBasePolygon, aLeft, aRight); + const attribute::LineStartEndAttribute aEmpty; + const primitive2d::PolygonStrokeArrowPrimitive2D aPLeft( + aLeft, + rStrokeArrowPrimitive.getLineAttribute(), + rStrokeArrowPrimitive.getStrokeAttribute(), + rStrokeArrowPrimitive.getStart(), + aEmpty); + const primitive2d::PolygonStrokeArrowPrimitive2D aPRight( + aRight, + rStrokeArrowPrimitive.getLineAttribute(), + rStrokeArrowPrimitive.getStrokeAttribute(), + aEmpty, + rStrokeArrowPrimitive.getEnd()); + + processBasePrimitive2D(aPLeft); + processBasePrimitive2D(aPRight); + } + else + { + // support SvtGraphicStroke MetaCommentAction + SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rBasePolygon, 0, + &rStrokeArrowPrimitive.getLineAttribute(), + &rStrokeArrowPrimitive.getStrokeAttribute(), + &rStrokeArrowPrimitive.getStart(), + &rStrokeArrowPrimitive.getEnd()); + + impStartSvtGraphicStroke(pSvtGraphicStroke); + process(rCandidate.get2DDecomposition(getViewInformation2D())); + impEndSvtGraphicStroke(pSvtGraphicStroke); + } + + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // direct draw of transformed BitmapEx primitive; use default processing + RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D : + { + // need to handle PolyPolygonBitmapPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END + const primitive2d::PolyPolygonBitmapPrimitive2D& rBitmapCandidate = static_cast< const primitive2d::PolyPolygonBitmapPrimitive2D& >(rCandidate); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon()); + + if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more use the splitted polygon and call recursively + const primitive2d::PolyPolygonBitmapPrimitive2D aSplitted( + aLocalPolyPolygon, + rBitmapCandidate.getFillBitmap()); + + processBasePrimitive2D(aSplitted); + } + else + { + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + aLocalPolyPolygon.transform(maCurrentTransformation); + // calculate transformation. Get real object size, all values in FillBitmapAttribute + // are relative to the unified object + const attribute::FillBitmapAttribute& rFillBitmapAttribute = rBitmapCandidate .getFillBitmap(); + const basegfx::B2DRange aOutlineRange(basegfx::tools::getRange(aLocalPolyPolygon)); + const basegfx::B2DVector aOutlineSize(aOutlineRange.getRange()); + + // get absolute values + const basegfx::B2DVector aFillBitmapSize(rFillBitmapAttribute.getSize() * aOutlineSize); + const basegfx::B2DPoint aFillBitmapTopLeft(rFillBitmapAttribute.getTopLeft() * aOutlineSize); + + // the scaling needs scale from pixel to logic coordinate system + const BitmapEx& rBitmapEx = rFillBitmapAttribute.getBitmapEx(); + Size aBmpSizePixel(rBitmapEx.GetSizePixel()); + + if(!aBmpSizePixel.Width()) + { + aBmpSizePixel.Width() = 1; + } + + if(!aBmpSizePixel.Height()) + { + aBmpSizePixel.Height() = 1; + } + + // setup transformation like in impgrfll + SvtGraphicFill::Transform aTransform; + + // scale values are divided by bitmap pixel sizes + aTransform.matrix[0] = aFillBitmapSize.getX() / aBmpSizePixel.Width(); + aTransform.matrix[4] = aFillBitmapSize.getY() / aBmpSizePixel.Height(); + + // translates are absolute + aTransform.matrix[2] = aFillBitmapTopLeft.getX(); + aTransform.matrix[5] = aFillBitmapTopLeft.getY(); + + // setup fill graphic like in impgrfll + Graphic aFillGraphic = Graphic(rBitmapEx); + aFillGraphic.SetPrefMapMode(MapMode(MAP_PIXEL)); + aFillGraphic.SetPrefSize(aBmpSizePixel); + + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillTexture, + aTransform, + rFillBitmapAttribute.getTiling(), + SvtGraphicFill::hatchSingle, + Color(), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + aFillGraphic); + } + + // Do use decomposition; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + process(rCandidate.get2DDecomposition(getViewInformation2D())); + impEndSvtGraphicFill(pSvtGraphicFill); + } + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D : + { + // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END + const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate = static_cast< const primitive2d::PolyPolygonHatchPrimitive2D& >(rCandidate); + const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch(); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + if(rFillHatchAttribute.isFillBackground()) + { + // with fixing #i111954# (see below) the possible background + // fill of a hatched object was lost.Generate a background fill + // primitive and render it + const primitive2d::Primitive2DReference xBackground( + new primitive2d::PolyPolygonColorPrimitive2D( + aLocalPolyPolygon, + rHatchCandidate.getBackgroundColor())); + + process(primitive2d::Primitive2DSequence(&xBackground, 1)); + } + + SvtGraphicFill* pSvtGraphicFill = 0; + aLocalPolyPolygon.transform(maCurrentTransformation); + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // re-create a VCL hatch as base data + SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle); + + switch(rFillHatchAttribute.getStyle()) + { + default: // attribute::HATCHSTYLE_SINGLE : + { + eHatch = SvtGraphicFill::hatchSingle; + break; + } + case attribute::HATCHSTYLE_DOUBLE : + { + eHatch = SvtGraphicFill::hatchDouble; + break; + } + case attribute::HATCHSTYLE_TRIPLE : + { + eHatch = SvtGraphicFill::hatchTriple; + break; + } + } + + SvtGraphicFill::Transform aTransform; + + // scale + aTransform.matrix[0] *= rFillHatchAttribute.getDistance(); + aTransform.matrix[4] *= rFillHatchAttribute.getDistance(); + + // rotate (was never correct in impgrfll anyways, use correct angle now) + aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle()); + aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle()); + aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle()); + aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle()); + + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillHatch, + aTransform, + false, + eHatch, + Color(rFillHatchAttribute.getColor()), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + Graphic()); + } + + // Do use decomposition; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + + // #i111954# do NOT use decomposition, but use direct VCL-command + // process(rCandidate.get2DDecomposition(getViewInformation2D())); + const PolyPolygon aToolsPolyPolygon(aLocalPolyPolygon); + const HatchStyle aHatchStyle( + attribute::HATCHSTYLE_SINGLE == rFillHatchAttribute.getStyle() ? HATCH_SINGLE : + attribute::HATCHSTYLE_DOUBLE == rFillHatchAttribute.getStyle() ? HATCH_DOUBLE : + HATCH_TRIPLE); + + mpOutputDevice->DrawHatch(aToolsPolyPolygon, + Hatch(aHatchStyle, + Color(rFillHatchAttribute.getColor()), + basegfx::fround(rFillHatchAttribute.getDistance()), + basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800))); + + impEndSvtGraphicFill(pSvtGraphicFill); + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D : + { + const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate = static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END + // it is safest to use the VCL OutputDevice::DrawGradient method which creates those. + // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon + Gradient aVCLGradient; + impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(), false); + aLocalPolyPolygon.transform(maCurrentTransformation); + + // #i82145# ATM VCL printing of gradients using curved shapes does not work, + // i submitted the bug with the given ID to THB. When that task is fixed it is + // necessary to again remove this subdivision since it decreases possible + // printing quality (not even resolution-dependent for now). THB will tell + // me when that task is fixed in the master + const PolyPolygon aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon)); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup gradient stuff like in like in impgrfll + SvtGraphicFill::GradientType eGrad(SvtGraphicFill::gradientLinear); + + switch(aVCLGradient.GetStyle()) + { + default : // GRADIENT_LINEAR: + case GRADIENT_AXIAL: + eGrad = SvtGraphicFill::gradientLinear; + break; + case GRADIENT_RADIAL: + case GRADIENT_ELLIPTICAL: + eGrad = SvtGraphicFill::gradientRadial; + break; + case GRADIENT_SQUARE: + case GRADIENT_RECT: + eGrad = SvtGraphicFill::gradientRectangular; + break; + } + + pSvtGraphicFill = new SvtGraphicFill( + aToolsPolyPolygon, + Color(), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillGradient, + SvtGraphicFill::Transform(), + false, + SvtGraphicFill::hatchSingle, + Color(), + eGrad, + aVCLGradient.GetStartColor(), + aVCLGradient.GetEndColor(), + aVCLGradient.GetSteps(), + Graphic()); + } + + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); + impEndSvtGraphicFill(pSvtGraphicFill); + + // NO usage of common own gradient randerer, not used ATM for VCL MetaFile, see text above + // RenderPolyPolygonGradientPrimitive2D(static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + aLocalPolyPolygon.transform(maCurrentTransformation); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup simple color fill stuff like in impgrfll + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(aPolygonColor), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillSolid, + SvtGraphicFill::Transform(), + false, + SvtGraphicFill::hatchSingle, + Color(), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + Graphic()); + } + + // set line and fill color + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + impEndSvtGraphicFill(pSvtGraphicFill); + + break; + } + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + { + // direct draw of MetaFile, use default pocessing + RenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // mask group. Special handling for MetaFiles. + const primitive2d::MaskPrimitive2D& rMaskCandidate = static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate); + + if(rMaskCandidate.getChildren().hasElements()) + { + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + + if(aMask.count()) + { + // prepare new mask polygon and rescue current one + aMask.transform(maCurrentTransformation); + const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon); + + if(maClipPolyPolygon.count()) + { + // there is already a clip polygon set; build clipped union of + // current mask polygon and new one + maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon( + aMask, + maClipPolyPolygon, + true, // #i106516# we want the inside of aMask, not the outside + false); + } + else + { + // use mask directly + maClipPolyPolygon = aMask; + } + + if(maClipPolyPolygon.count()) + { + // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!) + // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where + // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there + mpOutputDevice->Push(PUSH_CLIPREGION); + //mpOutputDevice->SetClipRegion(Region(PolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(maClipPolyPolygon)))); + mpOutputDevice->SetClipRegion(Region(PolyPolygon(maClipPolyPolygon))); + } + + // recursively paint content + process(rMaskCandidate.getChildren()); + + if(maClipPolyPolygon.count()) + { + // restore VCL clip region + mpOutputDevice->Pop(); + } + + // restore to rescued clip polygon + maClipPolyPolygon = aLastClipPolyPolygon; + } + else + { + // no mask, no clipping. recursively paint content + process(rMaskCandidate.getChildren()); + } + } + + break; + } + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D : + { + // modified color group. Force output to unified color. Use default pocessing. + RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D : + { + // HiddenGeometryPrimitive2D; to rebuilt the old MetaFile creation, it is necessary to + // not ignore them (as it was thought), but to add a MetaFile entry for them. + basegfx::B2DRange aInvisibleRange(rCandidate.getB2DRange(getViewInformation2D())); + + if(!aInvisibleRange.isEmpty()) + { + aInvisibleRange.transform(maCurrentTransformation); + const Rectangle aRectLogic( + (sal_Int32)floor(aInvisibleRange.getMinX()), (sal_Int32)floor(aInvisibleRange.getMinY()), + (sal_Int32)ceil(aInvisibleRange.getMaxX()), (sal_Int32)ceil(aInvisibleRange.getMaxY())); + + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawRect(aRectLogic); + } + + break; + } + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D : + { + // for metafile: Need to examine what the pure vcl version is doing here actually + // - uses DrawTransparent with metafile for content and a gradient + // - uses DrawTransparent for single PolyPoylgons directly. Can be detected by + // checking the content for single PolyPolygonColorPrimitive2D + const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate = static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate); + const primitive2d::Primitive2DSequence rContent = rUniTransparenceCandidate.getChildren(); + + if(rContent.hasElements()) + { + if(0.0 == rUniTransparenceCandidate.getTransparence()) + { + // not transparent at all, use content + process(rUniTransparenceCandidate.getChildren()); + } + else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0) + { + // try to identify a single PolyPolygonColorPrimitive2D in the + // content part of the transparence primitive + const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = 0; + static bool bForceToMetafile(false); + + if(!bForceToMetafile && 1 == rContent.getLength()) + { + const primitive2d::Primitive2DReference xReference(rContent[0]); + pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get()); + } + + // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and + // PolyPolygonBitmapPrimitive2D are derived from PolyPolygonColorPrimitive2D. + // Check also for correct ID to exclude derived implementations + if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID()) + { + // single transparent PolyPolygon identified, use directly + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor())); + basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + // now transform + aLocalPolyPolygon.transform(maCurrentTransformation); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup simple color with transparence fill stuff like in impgrfll + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(aPolygonColor), + rUniTransparenceCandidate.getTransparence(), + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillSolid, + SvtGraphicFill::Transform(), + false, + SvtGraphicFill::hatchSingle, + Color(), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + Graphic()); + } + + // set line and fill color + const sal_uInt16 nTransPercentVcl((sal_uInt16)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0)); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + mpOutputDevice->DrawTransparent( + PolyPolygon(aLocalPolyPolygon), + nTransPercentVcl); + impEndSvtGraphicFill(pSvtGraphicFill); + } + else + { + // svae old mfCurrentUnifiedTransparence and set new one + // so that contained SvtGraphicStroke may use the current one + const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence); + // #i105377# paint the content metafile opaque as the transparency gets + // split of into the gradient below + // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence(); + mfCurrentUnifiedTransparence = 0; + + // various content, create content-metafile + GDIMetaFile aContentMetafile; + const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile)); + + // restore mfCurrentUnifiedTransparence; it may have been used + // while processing the sub-content in impDumpToMetaFile + mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence; + + // create uniform VCL gradient for uniform transparency + Gradient aVCLGradient; + const sal_uInt8 nTransPercentVcl((sal_uInt8)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0)); + const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl); + + aVCLGradient.SetStyle(GRADIENT_LINEAR); + aVCLGradient.SetStartColor(aTransColor); + aVCLGradient.SetEndColor(aTransColor); + aVCLGradient.SetAngle(0); + aVCLGradient.SetBorder(0); + aVCLGradient.SetOfsX(0); + aVCLGradient.SetOfsY(0); + aVCLGradient.SetStartIntensity(100); + aVCLGradient.SetEndIntensity(100); + aVCLGradient.SetSteps(2); + + // render it to VCL + mpOutputDevice->DrawTransparent( + aContentMetafile, aPrimitiveRectangle.TopLeft(), + aPrimitiveRectangle.GetSize(), aVCLGradient); + } + } + } + + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : + { + // for metafile: Need to examine what the pure vcl version is doing here actually + // - uses DrawTransparent with metafile for content and a gradient + // i can detect this here with checking the gradient part for a single + // FillGradientPrimitive2D and reconstruct the gradient. + // If that detection goes wrong, i have to create an transparence-blended bitmap. Eventually + // do that in stripes, else RenderTransparencePrimitive2D may just be used + const primitive2d::TransparencePrimitive2D& rTransparenceCandidate = static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate); + const primitive2d::Primitive2DSequence rContent = rTransparenceCandidate.getChildren(); + const primitive2d::Primitive2DSequence rTransparence = rTransparenceCandidate.getTransparence(); + + if(rContent.hasElements() && rTransparence.hasElements()) + { + // try to identify a single FillGradientPrimitive2D in the + // transparence part of the primitive + const primitive2d::FillGradientPrimitive2D* pFiGradient = 0; + static bool bForceToBigTransparentVDev(false); + + if(!bForceToBigTransparentVDev && 1 == rTransparence.getLength()) + { + const primitive2d::Primitive2DReference xReference(rTransparence[0]); + pFiGradient = dynamic_cast< const primitive2d::FillGradientPrimitive2D* >(xReference.get()); + } + + // Check also for correct ID to exclude derived implementations + if(pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID()) + { + // various content, create content-metafile + GDIMetaFile aContentMetafile; + const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile)); + + // re-create a VCL-gradient from FillGradientPrimitive2D + Gradient aVCLGradient; + impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true); + + // render it to VCL + mpOutputDevice->DrawTransparent( + aContentMetafile, aPrimitiveRectangle.TopLeft(), + aPrimitiveRectangle.GetSize(), aVCLGradient); + } + else + { + // sub-transparence group. Draw to VDev first. + // this may get refined to tiling when resolution is too big here + + // need to avoid switching off MapMode stuff here; maybe need another + // tooling class, cannot just do the same as with the pixel renderer. + // Need to experiment... + + // Okay, basic implementation finished and tested. The DPI stuff was hard + // and not easy to find out that it's needed. + // Since this will not yet happen normally (as long as noone constructs + // transparence primitives with non-trivial transparence content) i will for now not + // refine to tiling here. + + basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D())); + aViewRange.transform(maCurrentTransformation); + const Rectangle aRectLogic( + (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()), + (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY())); + const Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic)); + const Size aSizePixel(aRectPixel.GetSize()); + const Point aEmptyPoint; + VirtualDevice aBufferDevice; + + if(aBufferDevice.SetOutputSizePixel(aSizePixel)) + { + // create and set MapModes for target devices + MapMode aNewMapMode(mpOutputDevice->GetMapMode()); + aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top())); + aBufferDevice.SetMapMode(aNewMapMode); + + // prepare view transformation for target renderers + // ATTENTION! Need to apply another scaling because of the potential DPI differences + // between Printer and VDev (mpOutputDevice and aBufferDevice here). + // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used. + basegfx::B2DHomMatrix aViewTransform(aBufferDevice.GetViewTransformation()); + const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MAP_INCH)); + const Size aDPINew(aBufferDevice.LogicToPixel(Size(1, 1), MAP_INCH)); + const double fDPIXChange((double)aDPIOld.getWidth() / (double)aDPINew.getWidth()); + const double fDPIYChange((double)aDPIOld.getHeight() / (double)aDPINew.getHeight()); + + if(!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0)) + { + aViewTransform.scale(fDPIXChange, fDPIYChange); + } + + // create view information and pixel renderer. Reuse known ViewInformation + // except new transformation and range + const geometry::ViewInformation2D aViewInfo( + getViewInformation2D().getObjectTransformation(), + aViewTransform, + aViewRange, + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + + VclPixelProcessor2D aBufferProcessor(aViewInfo, aBufferDevice); + + // draw content using pixel renderer + aBufferProcessor.process(rContent); + const Bitmap aBmContent(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel)); + + // draw transparence using pixel renderer + aBufferDevice.Erase(); + aBufferProcessor.process(rTransparence); + const AlphaMask aBmAlpha(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel)); + +#ifdef DBG_UTIL + static bool bDoSaveForVisualControl(false); + if(bDoSaveForVisualControl) + { + SvFileStream aNew(String(ByteString( "c:\\test.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); + aNew << aBmContent; + } +#endif + + // paint + mpOutputDevice->DrawBitmapEx( + aRectLogic.TopLeft(), + aRectLogic.GetSize(), + BitmapEx(aBmContent, aBmAlpha)); + } + } + } + + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // use default transform group pocessing + RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + { + // new XDrawPage for ViewInformation2D + RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + { + // use default marker array pocessing + RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + { + // use default point array pocessing + RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_CHARTPRIMITIVE2D : + { + // ChartPrimitive2D + const primitive2d::ChartPrimitive2D& rChartPrimitive = static_cast< const primitive2d::ChartPrimitive2D& >(rCandidate); + + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) + { + // fallback to decomposition (MetaFile) + process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); + } + break; + } + case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D : + { + // structured tag primitive + const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate = static_cast< const primitive2d::StructureTagPrimitive2D& >(rCandidate); + const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement()); + const bool bTagUsed(vcl::PDFWriter::NonStructElement != rTagElement); + + if(mpPDFExtOutDevData && bTagUsed) + { + // write start tag + mpPDFExtOutDevData->BeginStructureElement(rTagElement); + } + + // proccess childs normally + process(rStructureTagCandidate.getChildren()); + + if(mpPDFExtOutDevData && bTagUsed) + { + // write end tag + mpPDFExtOutDevData->EndStructureElement(); + } + + break; + } + case PRIMITIVE2D_ID_EPSPRIMITIVE2D : + { + RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate)); + break; + } + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + break; + } + } + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof |