drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 3872 ++++++------- drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx | 309 - solenv/clang-format/blacklist | 2 3 files changed, 2113 insertions(+), 2070 deletions(-)
New commits: commit 3561c53a1ab088816a8e3d1a1eb8e69f3f13aba1 Author: Tomaž Vajngerl <[email protected]> AuthorDate: Tue May 5 23:20:38 2020 +0200 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Tue May 5 23:20:38 2020 +0200 remove vclmetafileprocessor2d.{cxx,hxx} from clang-format blacklist Change-Id: I53f7660a22ed66ab7d50370d871f9d10d1bedc10 diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index d853820abbdc..231bf8bc9259 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -76,2215 +76,2241 @@ using namespace com::sun::star; // To be on the safe side with the old tools polygon, use slightly less than // the theoretical maximum (bad experiences with tools polygon) -#define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0) +#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) +// #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 nCount(rBasePolygon.count()); + const sal_uInt32 nHalfCount((nCount - 1) >> 1); - if(nCount) - { - const sal_uInt32 nHalfCount((nCount - 1) >> 1); + o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1); + o_aLeft.setClosed(false); - o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1); - o_aLeft.setClosed(false); + o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount); + o_aRight.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.isClosed()) + if (rBasePolygon.areControlPointsUsed()) { - o_aRight.append(rBasePolygon.getB2DPoint(0)); - - if(rBasePolygon.areControlPointsUsed()) - { - o_aRight.setControlPoints( - o_aRight.count() - 1, - rBasePolygon.getPrevControlPoint(0), - rBasePolygon.getNextControlPoint(0)); - } + 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 - void fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon) + else { - const sal_uInt32 nPolyCount(rPolyPolygon.count()); + o_aLeft.clear(); + o_aRight.clear(); + } +} - if(!nPolyCount) - return; +// #112245# helper to evtl. split filled polygons to maximum metafile point count +void fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon) +{ + const sal_uInt32 nPolyCount(rPolyPolygon.count()); - basegfx::B2DPolyPolygon aSplitted; + if (!nPolyCount) + return; - for(sal_uInt32 a(0); a < nPolyCount; a++) + 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) - 1); + } + else { - const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(a)); - const sal_uInt32 nPointCount(aCandidate.count()); - bool bNeedToSplit(false); + // compare with the maximum for simple point polygons + bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1); + } - if(aCandidate.areControlPointsUsed()) - { - // compare with the maximum for bezier curved polygons - bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1); - } - 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(bNeedToSplit) + if (aRange.getWidth() > aRange.getHeight()) { - // 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::utils::clipPolygonOnParallelAxis( - aCandidate, - false, - true, - aCenter.getX(), - false)); - const basegfx::B2DPolyPolygon aRight( - basegfx::utils::clipPolygonOnParallelAxis( - aCandidate, - false, - false, - aCenter.getX(), - false)); - - aSplitted.append(aLeft); - aSplitted.append(aRight); - } - else - { - // clip in top and bottom - const basegfx::B2DPolyPolygon aTop( - basegfx::utils::clipPolygonOnParallelAxis( - aCandidate, - true, - true, - aCenter.getY(), - false)); - const basegfx::B2DPolyPolygon aBottom( - basegfx::utils::clipPolygonOnParallelAxis( - aCandidate, - true, - false, - aCenter.getY(), - false)); - - aSplitted.append(aTop); - aSplitted.append(aBottom); - } + // clip in left and right + const basegfx::B2DPolyPolygon aLeft(basegfx::utils::clipPolygonOnParallelAxis( + aCandidate, false, true, aCenter.getX(), false)); + const basegfx::B2DPolyPolygon aRight(basegfx::utils::clipPolygonOnParallelAxis( + aCandidate, false, false, aCenter.getX(), false)); + + aSplitted.append(aLeft); + aSplitted.append(aRight); } else { - aSplitted.append(aCandidate); + // clip in top and bottom + const basegfx::B2DPolyPolygon aTop(basegfx::utils::clipPolygonOnParallelAxis( + aCandidate, true, true, aCenter.getY(), false)); + const basegfx::B2DPolyPolygon aBottom(basegfx::utils::clipPolygonOnParallelAxis( + aCandidate, true, false, aCenter.getY(), false)); + + aSplitted.append(aTop); + aSplitted.append(aBottom); } } - - if(aSplitted.count() != nPolyCount) + else { - rPolyPolygon = aSplitted; + aSplitted.append(aCandidate); } } - /** Filter input polypolygon for effectively empty sub-fills + if (aSplitted.count() != nPolyCount) + { + rPolyPolygon = aSplitted; + } +} + +/** Filter input polypolygon for effectively empty sub-fills - Needed to fix fdo#37559 + Needed to fix fdo#37559 - @param rPoly - tools::PolyPolygon to filter + @param rPoly + tools::PolyPolygon to filter - @return converted tools PolyPolygon, w/o one-point fills - */ - ::tools::PolyPolygon getFillPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly ) + @return converted tools PolyPolygon, w/o one-point fills + */ +tools::PolyPolygon getFillPolyPolygon(const ::basegfx::B2DPolyPolygon& rPoly) +{ + // filter input rPoly + basegfx::B2DPolyPolygon aPoly; + sal_uInt32 nCount(rPoly.count()); + for (sal_uInt32 i = 0; i < nCount; ++i) { - // filter input rPoly - basegfx::B2DPolyPolygon aPoly; - sal_uInt32 nCount(rPoly.count()); - for( sal_uInt32 i=0; i<nCount; ++i ) - { - const basegfx::B2DPolygon& aCandidate(rPoly.getB2DPolygon(i)); - if( !aCandidate.isClosed() || aCandidate.count() > 1 ) - aPoly.append(aCandidate); - } - return ::tools::PolyPolygon(aPoly); + const basegfx::B2DPolygon& aCandidate(rPoly.getB2DPolygon(i)); + if (!aCandidate.isClosed() || aCandidate.count() > 1) + aPoly.append(aCandidate); } + return tools::PolyPolygon(aPoly); +} } // end of anonymous namespace namespace drawinglayer::processor2d { - ::tools::Rectangle VclMetafileProcessor2D::impDumpToMetaFile( - const primitive2d::Primitive2DContainer& rContent, - GDIMetaFile& o_rContentMetafile) - { - // Prepare VDev, MetaFile and connections - OutputDevice* pLastOutputDevice = mpOutputDevice; - GDIMetaFile* pLastMetafile = mpMetaFile; - basegfx::B2DRange aPrimitiveRange(rContent.getB2DRange(getViewInformation2D())); - - // transform primitive range with current transformation (e.g shadow offset) - aPrimitiveRange.transform(maCurrentTransformation); - - const ::tools::Rectangle aPrimitiveRectangle( - basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()), - basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY())); - ScopedVclPtrInstance< VirtualDevice > aContentVDev; - MapMode aNewMapMode(pLastOutputDevice->GetMapMode()); - - mpOutputDevice = aContentVDev.get(); - mpMetaFile = &o_rContentMetafile; - aContentVDev->EnableOutput(false); - aContentVDev->SetMapMode(pLastOutputDevice->GetMapMode()); - o_rContentMetafile.Record(aContentVDev.get()); - 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) const - { - 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()))); - } +tools::Rectangle +VclMetafileProcessor2D::impDumpToMetaFile(const primitive2d::Primitive2DContainer& rContent, + GDIMetaFile& o_rContentMetafile) +{ + // Prepare VDev, MetaFile and connections + OutputDevice* pLastOutputDevice = mpOutputDevice; + GDIMetaFile* pLastMetafile = mpMetaFile; + basegfx::B2DRange aPrimitiveRange(rContent.getB2DRange(getViewInformation2D())); + + // transform primitive range with current transformation (e.g shadow offset) + aPrimitiveRange.transform(maCurrentTransformation); + + const tools::Rectangle aPrimitiveRectangle( + basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()), + basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY())); + ScopedVclPtrInstance<VirtualDevice> aContentVDev; + MapMode aNewMapMode(pLastOutputDevice->GetMapMode()); + + mpOutputDevice = aContentVDev.get(); + mpMetaFile = &o_rContentMetafile; + aContentVDev->EnableOutput(false); + aContentVDev->SetMapMode(pLastOutputDevice->GetMapMode()); + o_rContentMetafile.Record(aContentVDev.get()); + 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) const +{ + 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()); + 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); + // 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(GradientStyle::Linear); - break; - } - case attribute::GradientStyle::Axial : - { - o_rVCLGradient.SetStyle(GradientStyle::Axial); - break; - } - case attribute::GradientStyle::Radial : - { - o_rVCLGradient.SetStyle(GradientStyle::Radial); - break; - } - case attribute::GradientStyle::Elliptical : - { - o_rVCLGradient.SetStyle(GradientStyle::Elliptical); - break; - } - case attribute::GradientStyle::Square : - { - o_rVCLGradient.SetStyle(GradientStyle::Square); - break; - } - case attribute::GradientStyle::Rect : - { - o_rVCLGradient.SetStyle(GradientStyle::Rect); - break; - } - } + switch (rFiGrAtt.getStyle()) + { + default: // attribute::GradientStyle::Linear : + { + o_rVCLGradient.SetStyle(GradientStyle::Linear); + break; } - - void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const * pSvtGraphicFill) + case attribute::GradientStyle::Axial: { - if(pSvtGraphicFill && !mnSvtGraphicFillCount) - { - SvMemoryStream aMemStm; - - WriteSvtGraphicFill( aMemStm, *pSvtGraphicFill ); - mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.TellEnd())); - mnSvtGraphicFillCount++; - } + o_rVCLGradient.SetStyle(GradientStyle::Axial); + break; } - - void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const * pSvtGraphicFill) + case attribute::GradientStyle::Radial: { - if(pSvtGraphicFill && mnSvtGraphicFillCount) - { - mnSvtGraphicFillCount--; - mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END")); - } + o_rVCLGradient.SetStyle(GradientStyle::Radial); + break; } - - double VclMetafileProcessor2D::getTransformedLineWidth( double fWidth ) const + case attribute::GradientStyle::Elliptical: { - // #i113922# the LineWidth is duplicated in the MetaPolylineAction, - // and also inside the SvtGraphicStroke and needs transforming into - // the same space as its coordinates here cf. fdo#61789 - // This is a partial fix. When an object transformation is used which - // e.g. contains a scaleX != scaleY, an unproportional scaling will happen. - const basegfx::B2DVector aDiscreteUnit( maCurrentTransformation * basegfx::B2DVector( fWidth, 0.0 ) ); - - return aDiscreteUnit.getLength(); + o_rVCLGradient.SetStyle(GradientStyle::Elliptical); + break; } - - std::unique_ptr<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) + case attribute::GradientStyle::Square: + { + o_rVCLGradient.SetStyle(GradientStyle::Square); + break; + } + case attribute::GradientStyle::Rect: { - std::unique_ptr<SvtGraphicStroke> pRetval; + o_rVCLGradient.SetStyle(GradientStyle::Rect); + break; + } + } +} - if(rB2DPolygon.count() && !mnSvtGraphicStrokeCount) - { - basegfx::B2DPolygon aLocalPolygon(rB2DPolygon); - basegfx::BColor aStrokeColor; - basegfx::B2DPolyPolygon aStartArrow; - basegfx::B2DPolyPolygon aEndArrow; +void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill) +{ + if (pSvtGraphicFill && !mnSvtGraphicFillCount) + { + SvMemoryStream aMemStm; - if(pColor) - { - aStrokeColor = *pColor; - } - else if(pLineAttribute) - { - aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor()); - } + WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill); + mpMetaFile->AddAction(new MetaCommentAction( + "XPATHFILL_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), + aMemStm.TellEnd())); + mnSvtGraphicFillCount++; + } +} - // It IS needed to record the stroke color at all in the metafile, - // SvtGraphicStroke has NO entry for stroke color(!) - mpOutputDevice->SetLineColor(Color(aStrokeColor)); +void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill) +{ + if (pSvtGraphicFill && mnSvtGraphicFillCount) + { + mnSvtGraphicFillCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END")); + } +} - if(!aLocalPolygon.isClosed()) - { - double fPolyLength(0.0); - double fStart(0.0); - double fEnd(0.0); - - if(pStart && pStart->isActive()) - { - fPolyLength = basegfx::utils::getLength(aLocalPolygon); - - aStartArrow = basegfx::utils::createAreaGeometryForLineStartEnd( - aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), - fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart); - } - - if(pEnd && pEnd->isActive()) - { - if(basegfx::fTools::equalZero(fPolyLength)) - { - fPolyLength = basegfx::utils::getLength(aLocalPolygon); - } - - aEndArrow = basegfx::utils::createAreaGeometryForLineStartEnd( - aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), - fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, &fEnd); - } - - if(0.0 != fStart || 0.0 != fEnd) - { - // build new poly, consume something from old poly - aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart, fPolyLength - fEnd, fPolyLength); - } - } +double VclMetafileProcessor2D::getTransformedLineWidth(double fWidth) const +{ + // #i113922# the LineWidth is duplicated in the MetaPolylineAction, + // and also inside the SvtGraphicStroke and needs transforming into + // the same space as its coordinates here cf. fdo#61789 + // This is a partial fix. When an object transformation is used which + // e.g. contains a scaleX != scaleY, an unproportional scaling will happen. + const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation + * basegfx::B2DVector(fWidth, 0.0)); + + return aDiscreteUnit.getLength(); +} + +std::unique_ptr<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) +{ + std::unique_ptr<SvtGraphicStroke> pRetval; - SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone); - SvtGraphicStroke::CapType eCap(SvtGraphicStroke::capButt); - double fLineWidth(0.0); - double fMiterLength(0.0); - SvtGraphicStroke::DashArray aDashArray; + if (rB2DPolygon.count() && !mnSvtGraphicStrokeCount) + { + basegfx::B2DPolygon aLocalPolygon(rB2DPolygon); + basegfx::BColor aStrokeColor; + basegfx::B2DPolyPolygon aStartArrow; + basegfx::B2DPolyPolygon aEndArrow; - if(pLineAttribute) - { - fLineWidth = fMiterLength = getTransformedLineWidth( pLineAttribute->getWidth() ); - - // get Join - switch(pLineAttribute->getLineJoin()) - { - case basegfx::B2DLineJoin::NONE : - { - eJoin = SvtGraphicStroke::joinNone; - break; - } - case basegfx::B2DLineJoin::Bevel : - { - eJoin = SvtGraphicStroke::joinBevel; - break; - } - case basegfx::B2DLineJoin::Miter : - { - eJoin = SvtGraphicStroke::joinMiter; - // ATM 15 degrees is assumed - fMiterLength /= rtl::math::sin(basegfx::deg2rad(15.0)); - break; - } - case basegfx::B2DLineJoin::Round : - { - eJoin = SvtGraphicStroke::joinRound; - break; - } - } - - // get stroke - switch(pLineAttribute->getLineCap()) - { - default: /* css::drawing::LineCap_BUTT */ - { - eCap = SvtGraphicStroke::capButt; - break; - } - case css::drawing::LineCap_ROUND: - { - eCap = SvtGraphicStroke::capRound; - break; - } - case css::drawing::LineCap_SQUARE: - { - eCap = SvtGraphicStroke::capSquare; - break; - } - } - } + if (pColor) + { + aStrokeColor = *pColor; + } + else if (pLineAttribute) + { + aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor()); + } - if(pStrokeAttribute) - { - // copy dash array - aDashArray = pStrokeAttribute->getDotDashArray(); - } + // It IS needed to record the stroke color at all in the metafile, + // SvtGraphicStroke has NO entry for stroke color(!) + mpOutputDevice->SetLineColor(Color(aStrokeColor)); - // #i101734# apply current object transformation to created geometry. - // This is a partial fix. When an 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 - aLocalPolygon.transform(maCurrentTransformation); - aStartArrow.transform(maCurrentTransformation); - aEndArrow.transform(maCurrentTransformation); - - pRetval.reset(new SvtGraphicStroke( - ::tools::Polygon(aLocalPolygon), - ::tools::PolyPolygon(aStartArrow), - ::tools::PolyPolygon(aEndArrow), - mfCurrentUnifiedTransparence, - fLineWidth, - eCap, - eJoin, - fMiterLength, - aDashArray)); - } + if (!aLocalPolygon.isClosed()) + { + double fPolyLength(0.0); + double fStart(0.0); + double fEnd(0.0); - return pRetval; - } + if (pStart && pStart->isActive()) + { + fPolyLength = basegfx::utils::getLength(aLocalPolygon); - void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const * pSvtGraphicStroke) - { - if(pSvtGraphicStroke && !mnSvtGraphicStrokeCount) + aStartArrow = basegfx::utils::createAreaGeometryForLineStartEnd( + aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), + fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart); + } + + if (pEnd && pEnd->isActive()) { - SvMemoryStream aMemStm; + if (basegfx::fTools::equalZero(fPolyLength)) + { + fPolyLength = basegfx::utils::getLength(aLocalPolygon); + } - WriteSvtGraphicStroke( aMemStm, *pSvtGraphicStroke ); - mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.TellEnd())); - mnSvtGraphicStrokeCount++; + aEndArrow = basegfx::utils::createAreaGeometryForLineStartEnd( + aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), fPolyLength, + pEnd->isCentered() ? 0.5 : 0.0, &fEnd); } - } - void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const * pSvtGraphicStroke) - { - if(pSvtGraphicStroke && mnSvtGraphicStrokeCount) + if (0.0 != fStart || 0.0 != fEnd) { - mnSvtGraphicStrokeCount--; - mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END")); + // build new poly, consume something from old poly + aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart, + fPolyLength - fEnd, fPolyLength); } } - void VclMetafileProcessor2D::popStructureElement(vcl::PDFWriter::StructElement eElem) - { - if (!maListElements.empty() && maListElements.top() == eElem) - { - maListElements.pop(); - mpPDFExtOutDevData->EndStructureElement(); - } - } + SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone); + SvtGraphicStroke::CapType eCap(SvtGraphicStroke::capButt); + double fLineWidth(0.0); + double fMiterLength(0.0); + SvtGraphicStroke::DashArray aDashArray; - void VclMetafileProcessor2D::popListItem() + if (pLineAttribute) { - popStructureElement(vcl::PDFWriter::LIBody); - popStructureElement(vcl::PDFWriter::ListItem); - } + fLineWidth = fMiterLength = getTransformedLineWidth(pLineAttribute->getWidth()); - void VclMetafileProcessor2D::popList() - { - popListItem(); - popStructureElement(vcl::PDFWriter::List); - } - - // init static break iterator - uno::Reference< css::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())), - mnCurrentOutlineLevel(-1), - mbInListItem(false), - mbBulletPresent(false) - { - 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 its gradient objects. - Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D, - map it back to the corresponding tools 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_POLYPOLYGONGRAPHICPRIMITIVE2D, - 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 tools::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 a 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 an 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, recursively 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 - Animation 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 explicitly 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 than the name - may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself, - the lowest movement plane 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()) + // get Join + switch (pLineAttribute->getLineJoin()) { - 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 : - { - processGraphicPrimitive2D(static_cast<const primitive2d::GraphicPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : - { - processControlPrimitive2D(static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D : - { - processTextHierarchyFieldPrimitive2D(static_cast<const primitive2d::TextHierarchyFieldPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D : - { - processTextHierarchyLinePrimitive2D(static_cast<const primitive2d::TextHierarchyLinePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D : - { - processTextHierarchyBulletPrimitive2D(static_cast<const primitive2d::TextHierarchyBulletPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D : - { - processTextHierarchyParagraphPrimitive2D(static_cast<const primitive2d::TextHierarchyParagraphPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D : - { - processTextHierarchyBlockPrimitive2D(static_cast<const primitive2d::TextHierarchyBlockPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : - case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : - { - // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate - processTextSimplePortionPrimitive2D(static_cast<const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : - { - processPolygonHairlinePrimitive2D(static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D : - { - processPolygonStrokePrimitive2D(static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D : - { - processPolygonStrokeArrowPrimitive2D(static_cast<const primitive2d::PolygonStrokeArrowPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : - { - // direct draw of transformed BitmapEx primitive; use default processing, but without - // former testing if graphic content is inside discrete local viewport; this is not - // setup for metafile targets (metafile renderer tries to render in logic coordinates, - // the mapping is kept to the OutputDevice for better Metafile recording) - RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D : - { - processPolyPolygonGraphicPrimitive2D(static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D : - { - processPolyPolygonHatchPrimitive2D(static_cast<const primitive2d::PolyPolygonHatchPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D : - { - processPolyPolygonGradientPrimitive2D(static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : - { - processPolyPolygonColorPrimitive2D(static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_MASKPRIMITIVE2D : - { - processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); - 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_UNIFIEDTRANSPARENCEPRIMITIVE2D : - { - processUnifiedTransparencePrimitive2D(static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : - { - processTransparencePrimitive2D(static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : - { - // use default transform group pocessing - RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + case basegfx::B2DLineJoin::NONE: { - // new XDrawPage for ViewInformation2D - RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinNone; break; } - case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + case basegfx::B2DLineJoin::Bevel: { - // use default marker array pocessing - RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinBevel; break; } - case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + case basegfx::B2DLineJoin::Miter: { - // use default point array pocessing - RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinMiter; + // ATM 15 degrees is assumed + fMiterLength /= rtl::math::sin(basegfx::deg2rad(15.0)); break; } - case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D : + case basegfx::B2DLineJoin::Round: { - processStructureTagPrimitive2D(static_cast<const primitive2d::StructureTagPrimitive2D&>(rCandidate)); + eJoin = SvtGraphicStroke::joinRound; break; } - case PRIMITIVE2D_ID_EPSPRIMITIVE2D : + } + + // get stroke + switch (pLineAttribute->getLineCap()) + { + default: /* css::drawing::LineCap_BUTT */ { - RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate)); + eCap = SvtGraphicStroke::capButt; break; } - case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D : + case css::drawing::LineCap_ROUND: { - RenderObjectInfoPrimitive2D(static_cast< const primitive2d::ObjectInfoPrimitive2D& >(rCandidate)); + eCap = SvtGraphicStroke::capRound; break; } - default : + case css::drawing::LineCap_SQUARE: { - // process recursively - process(rCandidate); + eCap = SvtGraphicStroke::capSquare; break; } } } - void VclMetafileProcessor2D::processGraphicPrimitive2D(const primitive2d::GraphicPrimitive2D& rGraphicPrimitive) + if (pStrokeAttribute) { - bool bUsingPDFExtOutDevData(false); - basegfx::B2DVector aTranslate, aScale; - static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore + // copy dash array + aDashArray = pStrokeAttribute->getDotDashArray(); + } - if(mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport) - { - // emulate data handling from UnoControlPDFExportContact, original see - // svtools/source/graphic/grfmgr.cxx - const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic(); + // #i101734# apply current object transformation to created geometry. + // This is a partial fix. When an 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 + aLocalPolygon.transform(maCurrentTransformation); + aStartArrow.transform(maCurrentTransformation); + aEndArrow.transform(maCurrentTransformation); + + pRetval.reset( + new SvtGraphicStroke(tools::Polygon(aLocalPolygon), tools::PolyPolygon(aStartArrow), + tools::PolyPolygon(aEndArrow), mfCurrentUnifiedTransparence, + fLineWidth, eCap, eJoin, fMiterLength, aDashArray)); + } - if(rGraphic.IsGfxLink()) - { - 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(); - } - } - } - } + return pRetval; +} - // process recursively and add MetaFile comment - process(rGraphicPrimitive); +void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke) +{ + if (pSvtGraphicStroke && !mnSvtGraphicStrokeCount) + { + SvMemoryStream aMemStm; - if(!bUsingPDFExtOutDevData) - return; + WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke); + mpMetaFile->AddAction(new MetaCommentAction( + "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), + aMemStm.TellEnd())); + mnSvtGraphicStrokeCount++; + } +} - // 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 ::tools::Rectangle aCurrentRect( - sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())), - sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY()))); +void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke) +{ + if (pSvtGraphicStroke && mnSvtGraphicStrokeCount) + { + mnSvtGraphicStrokeCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END")); + } +} + +void VclMetafileProcessor2D::popStructureElement(vcl::PDFWriter::StructElement eElem) +{ + if (!maListElements.empty() && maListElements.top() == eElem) + { + maListElements.pop(); + mpPDFExtOutDevData->EndStructureElement(); + } +} + +void VclMetafileProcessor2D::popListItem() +{ + popStructureElement(vcl::PDFWriter::LIBody); + popStructureElement(vcl::PDFWriter::ListItem); +} + +void VclMetafileProcessor2D::popList() +{ + popListItem(); + popStructureElement(vcl::PDFWriter::List); +} + +// init static break iterator +uno::Reference<css::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())) + , mnCurrentOutlineLevel(-1) + , mbInListItem(false) + , mbBulletPresent(false) +{ + 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 its gradient objects. + Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D, + map it back to the corresponding tools 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_POLYPOLYGONGRAPHICPRIMITIVE2D, + 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 tools::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 a 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 an 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, recursively 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 + Animation 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 explicitly 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 than the name + may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself, + the lowest movement plane 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: + { + processGraphicPrimitive2D( + static_cast<const primitive2d::GraphicPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: + { + processControlPrimitive2D( + static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D: + { + processTextHierarchyFieldPrimitive2D( + static_cast<const primitive2d::TextHierarchyFieldPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D: + { + processTextHierarchyLinePrimitive2D( + static_cast<const primitive2d::TextHierarchyLinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D: + { + processTextHierarchyBulletPrimitive2D( + static_cast<const primitive2d::TextHierarchyBulletPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D: + { + processTextHierarchyParagraphPrimitive2D( + static_cast<const primitive2d::TextHierarchyParagraphPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D: + { + processTextHierarchyBlockPrimitive2D( + static_cast<const primitive2d::TextHierarchyBlockPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: + { + // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate + processTextSimplePortionPrimitive2D( + static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: + { + processPolygonHairlinePrimitive2D( + static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + processPolygonStrokePrimitive2D( + static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D: + { + processPolygonStrokeArrowPrimitive2D( + static_cast<const primitive2d::PolygonStrokeArrowPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: + { + // direct draw of transformed BitmapEx primitive; use default processing, but without + // former testing if graphic content is inside discrete local viewport; this is not + // setup for metafile targets (metafile renderer tries to render in logic coordinates, + // the mapping is kept to the OutputDevice for better Metafile recording) + RenderBitmapPrimitive2D(static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: + { + processPolyPolygonGraphicPrimitive2D( + static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D: + { + processPolyPolygonHatchPrimitive2D( + static_cast<const primitive2d::PolyPolygonHatchPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D: + { + processPolyPolygonGradientPrimitive2D( + static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: + { + processPolyPolygonColorPrimitive2D( + static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D: + { + processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); + 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_UNIFIEDTRANSPARENCEPRIMITIVE2D: + { + processUnifiedTransparencePrimitive2D( + static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: + { + processTransparencePrimitive2D( + static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); + 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_STRUCTURETAGPRIMITIVE2D: + { + processStructureTagPrimitive2D( + static_cast<const primitive2d::StructureTagPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_EPSPRIMITIVE2D: + { + RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D: + { + RenderObjectInfoPrimitive2D( + static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate)); + break; + } + default: + { + // process recursively + process(rCandidate); + break; + } + } +} + +void VclMetafileProcessor2D::processGraphicPrimitive2D( + const primitive2d::GraphicPrimitive2D& rGraphicPrimitive) +{ + bool bUsingPDFExtOutDevData(false); + basegfx::B2DVector aTranslate, aScale; + static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore + + if (mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport) + { + // emulate data handling from UnoControlPDFExportContact, original see + // svtools/source/graphic/grfmgr.cxx + const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic(); + + if (rGraphic.IsGfxLink()) + { const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr(); - // fdo#72530 don't pass empty Rectangle to EndGroup - ::tools::Rectangle aCropRect(aCurrentRect); - if(rAttr.IsCropped()) + if (!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted()) { - // 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 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)) { - const MapMode aMapMode100thmm(MapUnit::Map100thMM); - const Size aBitmapSize(OutputDevice::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; - } + bUsingPDFExtOutDevData = true; + mpPDFExtOutDevData->BeginGroup(); } + } + } + } - // 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)); + // process recursively and add MetaFile comment + process(rGraphicPrimitive); + + if (!bUsingPDFExtOutDevData) + return; + + // 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 tools::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(); + // fdo#72530 don't pass empty Rectangle to EndGroup + tools::Rectangle aCropRect(aCurrentRect); + + 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); - aCropRect = ::tools::Rectangle( - sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())), - sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY()))); + { + const MapMode aMapMode100thmm(MapUnit::Map100thMM); + const Size aBitmapSize(OutputDevice::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; } - // Create image alternative description from ObjectInfoPrimitive2D info - // for PDF export - if(mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D()) + if (!basegfx::fTools::equalZero(fDivY)) { - OUString aAlternateDescription; + fFactorY = aScale.getY() / fDivY; + } + } - if(!getObjectInfoPrimitive2D()->getTitle().isEmpty()) - { - aAlternateDescription += getObjectInfoPrimitive2D()->getTitle(); - } + // 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 = tools::Rectangle( + sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())), + sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY()))); + } - if(!getObjectInfoPrimitive2D()->getDesc().isEmpty()) - { - if(!aAlternateDescription.isEmpty()) - { - aAlternateDescription += " - "; - } + // Create image alternative description from ObjectInfoPrimitive2D info + // for PDF export + if (mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D()) + { + OUString aAlternateDescription; - aAlternateDescription += getObjectInfoPrimitive2D()->getDesc(); - } + if (!getObjectInfoPrimitive2D()->getTitle().isEmpty()) + { + aAlternateDescription += getObjectInfoPrimitive2D()->getTitle(); + } - // Use SetAlternateText to set it. This will work as long as some - // structure is used (see PDFWriterImpl::setAlternateText and - // m_nCurrentStructElement - tagged PDF export works with this in - // Draw/Impress/Writer, but not in Calc due to too less structure...) - //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..? - if(!aAlternateDescription.isEmpty()) - { - mpPDFExtOutDevData->SetAlternateText(aAlternateDescription); - } + if (!getObjectInfoPrimitive2D()->getDesc().isEmpty()) + { + if (!aAlternateDescription.isEmpty()) + { + aAlternateDescription += " - "; } - // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped - // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded, - // uncropped region. Thus, correct order is aCropRect, aCurrentRect - mpPDFExtOutDevData->EndGroup( - rGraphicPrimitive.getGraphicObject().GetGraphic(), - rAttr.GetTransparency(), - aCropRect, - aCurrentRect); + aAlternateDescription += getObjectInfoPrimitive2D()->getDesc(); } - void VclMetafileProcessor2D::processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive) + // Use SetAlternateText to set it. This will work as long as some + // structure is used (see PDFWriterImpl::setAlternateText and + // m_nCurrentStructElement - tagged PDF export works with this in + // Draw/Impress/Writer, but not in Calc due to too less structure...) + //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..? + if (!aAlternateDescription.isEmpty()) { - const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl()); - bool bIsPrintableControl(false); + mpPDFExtOutDevData->SetAlternateText(aAlternateDescription); + } + } - // find out if control is printable - if(rXControl.is()) + // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped + // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded, + // uncropped region. Thus, correct order is aCropRect, aCurrentRect + mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(), + rAttr.GetTransparency(), aCropRect, aCurrentRect); +} + +void VclMetafileProcessor2D::processControlPrimitive2D( + const primitive2d::ControlPrimitive2D& rControlPrimitive) +{ + 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 OUString sPrintablePropertyName("Printable"); + + if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName)) { - 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 OUString sPrintablePropertyName("Printable"); - - if(xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName)) - { - OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) >>= bIsPrintableControl); - } - } - catch(const uno::Exception&) - { - TOOLS_WARN_EXCEPTION("drawinglayer", "VclMetafileProcessor2D: No access to printable flag of Control"); - } + OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) + >>= bIsPrintableControl); } + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("drawinglayer", + "VclMetafileProcessor2D: No access to printable flag of Control"); + } + } - // PDF export and printing only for printable controls - if(!bIsPrintableControl) - return; + // PDF export and printing only for printable controls + if (!bIsPrintableControl) + return; - const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); - bool bDoProcessRecursively(true); + 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::unique_ptr< vcl::PDFWriter::AnyWidget > pPDFControl( - ::toolkitform::describePDFControl( rXControl, *mpPDFExtOutDevData ) ); + 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::unique_ptr<vcl::PDFWriter::AnyWidget> pPDFControl( + ::toolkitform::describePDFControl(rXControl, *mpPDFExtOutDevData)); - if (pPDFControl) - { - // still need to fill in the location (is a class Rectangle) - const basegfx::B2DRange aRangeLogic(rControlPrimitive.getB2DRange(getViewInformation2D())); - const ::tools::Rectangle aRectLogic( - static_cast<sal_Int32>(floor(aRangeLogic.getMinX())), static_cast<sal_Int32>(floor(aRangeLogic.getMinY())), - static_cast<sal_Int32>(ceil(aRangeLogic.getMaxX())), static_cast<sal_Int32>(ceil(aRangeLogic.getMaxY()))); - pPDFControl->Location = aRectLogic; - - Size aFontSize(pPDFControl->TextFont.GetFontSize()); - aFontSize = OutputDevice::LogicToLogic(aFontSize, MapMode(MapUnit::MapPoint), mpOutputDevice->GetMapMode()); - pPDFControl->TextFont.SetFontSize(aFontSize); - - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form); - mpPDFExtOutDevData->CreateControl(*pPDFControl); - 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. - } - } + if (pPDFControl) + { + // still need to fill in the location (is a class Rectangle) + const basegfx::B2DRange aRangeLogic( + rControlPrimitive.getB2DRange(getViewInformation2D())); + const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aRangeLogic.getMinX())), + static_cast<sal_Int32>(floor(aRangeLogic.getMinY())), + static_cast<sal_Int32>(ceil(aRangeLogic.getMaxX())), + static_cast<sal_Int32>(ceil(aRangeLogic.getMaxY()))); + pPDFControl->Location = aRectLogic; + + Size aFontSize(pPDFControl->TextFont.GetFontSize()); + aFontSize = OutputDevice::LogicToLogic(aFontSize, MapMode(MapUnit::MapPoint), + mpOutputDevice->GetMapMode()); + pPDFControl->TextFont.SetFontSize(aFontSize); + + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form); + mpPDFExtOutDevData->CreateControl(*pPDFControl); + 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& ) - { - TOOLS_WARN_EXCEPTION("drawinglayer", "VclMetafileProcessor2D: Printing of Control failed"); - } - } + // #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()); - // process recursively if not done yet to export as decomposition (bitmap) - if(bDoProcessRecursively) + if (xNewGraphics.is()) { - process(rControlPrimitive); + // 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()), ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
