diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2020-05-05 23:20:38 +0200 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2020-05-09 21:08:28 +0200 |
commit | 9e2a9f4151babc6cb22553798ee70f7f623924db (patch) | |
tree | 26660371fb3096e4b46dbb75cef6151f3f522624 | |
parent | 3b0de78ed85c4944d74eaaa55a8f24a63dc6ea04 (diff) |
remove vclmetafileprocessor2d.{cxx,hxx} from clang-format blacklist
Change-Id: I53f7660a22ed66ab7d50370d871f9d10d1bedc10
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93825
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r-- | drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 3866 | ||||
-rw-r--r-- | drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx | 309 | ||||
-rw-r--r-- | solenv/clang-format/blacklist | 2 |
3 files changed, 2110 insertions, 2067 deletions
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(); + } +} + +// #112245# helper to evtl. split filled polygons to maximum metafile point count +void fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon) +{ + const sal_uInt32 nPolyCount(rPolyPolygon.count()); + + if (!nPolyCount) + return; - if(!nPolyCount) - return; + basegfx::B2DPolyPolygon aSplitted; - 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); - for(sal_uInt32 a(0); a < nPolyCount; a++) + if (aCandidate.areControlPointsUsed()) { - const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(a)); - const sal_uInt32 nPointCount(aCandidate.count()); - bool bNeedToSplit(false); + // 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(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 : + case basegfx::B2DLineJoin::NONE: { - // use default transform group pocessing - RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinNone; break; } - case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + case basegfx::B2DLineJoin::Bevel: { - // new XDrawPage for ViewInformation2D - RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinBevel; break; } - case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + case basegfx::B2DLineJoin::Miter: { - // use default marker array pocessing - RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinMiter; + // ATM 15 degrees is assumed + fMiterLength /= rtl::math::sin(basegfx::deg2rad(15.0)); break; } - case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + case basegfx::B2DLineJoin::Round: { - // use default point array pocessing - RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); + eJoin = SvtGraphicStroke::joinRound; break; } - case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D : - { - processStructureTagPrimitive2D(static_cast<const primitive2d::StructureTagPrimitive2D&>(rCandidate)); - 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 - - if(mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport) - { - // emulate data handling from UnoControlPDFExportContact, original see - // svtools/source/graphic/grfmgr.cxx - const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic(); + // copy dash array + aDashArray = pStrokeAttribute->getDotDashArray(); + } - 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(); - } - } - } - } + // #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)); + } - // process recursively and add MetaFile comment - process(rGraphicPrimitive); + return pRetval; +} - if(!bUsingPDFExtOutDevData) - return; +void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke) +{ + if (pSvtGraphicStroke && !mnSvtGraphicStrokeCount) + { + SvMemoryStream aMemStm; - // 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); + WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke); + mpMetaFile->AddAction(new MetaCommentAction( + "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), + aMemStm.TellEnd())); + mnSvtGraphicStrokeCount++; + } +} - 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); +void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke) +{ + if (pSvtGraphicStroke && mnSvtGraphicStrokeCount) + { + mnSvtGraphicStrokeCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END")); + } +} - { - 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; - } - } +void VclMetafileProcessor2D::popStructureElement(vcl::PDFWriter::StructElement eElem) +{ + if (!maListElements.empty() && maListElements.top() == eElem) + { + maListElements.pop(); + mpPDFExtOutDevData->EndStructureElement(); + } +} - // 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)); +void VclMetafileProcessor2D::popListItem() +{ + popStructureElement(vcl::PDFWriter::LIBody); + popStructureElement(vcl::PDFWriter::ListItem); +} - aCropRect = ::tools::Rectangle( - sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())), - sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY()))); - } +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; + } + } +} - // Create image alternative description from ObjectInfoPrimitive2D info - // for PDF export - if(mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D()) - { - OUString aAlternateDescription; +void VclMetafileProcessor2D::processGraphicPrimitive2D( + const primitive2d::GraphicPrimitive2D& rGraphicPrimitive) +{ + bool bUsingPDFExtOutDevData(false); + basegfx::B2DVector aTranslate, aScale; + static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore - if(!getObjectInfoPrimitive2D()->getTitle().isEmpty()) - { - aAlternateDescription += getObjectInfoPrimitive2D()->getTitle(); - } + if (mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport) + { + // emulate data handling from UnoControlPDFExportContact, original see + // svtools/source/graphic/grfmgr.cxx + const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic(); - if(!getObjectInfoPrimitive2D()->getDesc().isEmpty()) - { - if(!aAlternateDescription.isEmpty()) - { - aAlternateDescription += " - "; - } + if (rGraphic.IsGfxLink()) + { + const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr(); - aAlternateDescription += getObjectInfoPrimitive2D()->getDesc(); - } + if (!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted()) + { + const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform(); + double fRotate, fShearX; + rTransform.decompose(aScale, aTranslate, fRotate, fShearX); - // 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()) + if (basegfx::fTools::equalZero(fRotate) && (aScale.getX() > 0.0) + && (aScale.getY() > 0.0)) { - mpPDFExtOutDevData->SetAlternateText(aAlternateDescription); + bUsingPDFExtOutDevData = true; + mpPDFExtOutDevData->BeginGroup(); } } - - // #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); + // 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); - // find out if control is printable - if(rXControl.is()) + { + 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)) { - 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"); - } + fFactorX = aScale.getX() / fDivX; } - // PDF export and printing only for printable controls - if(!bIsPrintableControl) - return; + if (!basegfx::fTools::equalZero(fDivY)) + { + fFactorY = aScale.getY() / fDivY; + } + } - const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); - bool bDoProcessRecursively(true); + // 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(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 ) ); + // Create image alternative description from ObjectInfoPrimitive2D info + // for PDF export + if (mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D()) + { + OUString aAlternateDescription; - 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 (!getObjectInfoPrimitive2D()->getTitle().isEmpty()) + { + aAlternateDescription += getObjectInfoPrimitive2D()->getTitle(); + } - // #i93169# used flag the wrong way; true means that nothing was done yet - if(bDoProcessRecursively) + if (!getObjectInfoPrimitive2D()->getDesc().isEmpty()) + { + if (!aAlternateDescription.isEmpty()) { - // 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"); - } + aAlternateDescription += " - "; } - // process recursively if not done yet to export as decomposition (bitmap) - if(bDoProcessRecursively) - { - process(rControlPrimitive); - } + aAlternateDescription += getObjectInfoPrimitive2D()->getDesc(); } - void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive) + // 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()) { - // 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 OString aCommentStringCommon("FIELD_SEQ_BEGIN"); - const OString aCommentStringPage("FIELD_SEQ_BEGIN;PageField"); - const OString aCommentStringEnd("FIELD_SEQ_END"); - OUString aURL; + mpPDFExtOutDevData->SetAlternateText(aAlternateDescription); + } + } - 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 : - { - aURL = rFieldPrimitive.getValue("URL"); + // #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); +} - if (!aURL.isEmpty()) - { - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()), 2 * aURL.getLength())); - } +void VclMetafileProcessor2D::processControlPrimitive2D( + const primitive2d::ControlPrimitive2D& rControlPrimitive) +{ + const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); + bool bIsPrintableControl(false); - break; - } + // 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)) + { + OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) + >>= bIsPrintableControl); } + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("drawinglayer", + "VclMetafileProcessor2D: No access to printable flag of Control"); + } + } - // process recursively - primitive2d::Primitive2DContainer rContent; - rFieldPrimitive.get2DDecomposition(rContent, getViewInformation2D()); - process(rContent); + // PDF export and printing only for printable controls + if (!bIsPrintableControl) + return; - // for the end comment the type is not relevant yet, they are all the same. Just add. - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd)); + const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); + bool bDoProcessRecursively(true); - if(!(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType())) - return; + 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)); - // emulate data handling from ImpEditEngine::Paint - const basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D())); - const ::tools::Rectangle aRectLogic( - static_cast<sal_Int32>(floor(aViewRange.getMinX())), static_cast<sal_Int32>(floor(aViewRange.getMinY())), - static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); - vcl::PDFExtOutDevBookmarkEntry aBookmark; - aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic); - aBookmark.aBookmark = aURL; - std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks(); - rBookmarks.push_back( aBookmark ); + 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; } - - void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D(const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive) + else { - const OString aCommentString("XTEXT_EOL"); - - // process recursively and add MetaFile comment - process(rLinePrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + // PDF export did not work, try simple output. + // Fallback to printer output by not setting bDoProcessRecursively + // to false. } + } - void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive) + // #i93169# used flag the wrong way; true means that nothing was done yet + if (bDoProcessRecursively) + { + // printer output + try { - // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The - // "XTEXT_EOC" is used, use here, too. - const OString aCommentString("XTEXT_EOC"); + // 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()); - // this is a part of list item, start LILabel ( = bullet) - if(mbInListItem) + if (xNewGraphics.is()) { - maListElements.push(vcl::PDFWriter::LILabel); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel); + // 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"); + } + } - // process recursively and add MetaFile comment - process(rBulletPrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + // process recursively if not done yet to export as decomposition (bitmap) + if (bDoProcessRecursively) + { + process(rControlPrimitive); + } +} - if(mbInListItem) - { - if (maListElements.top() == vcl::PDFWriter::LILabel) - { - maListElements.pop(); - mpPDFExtOutDevData->EndStructureElement(); // end LILabel - mbBulletPresent = true; - } - } +void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D( + const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive) +{ + // 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 OString aCommentStringCommon("FIELD_SEQ_BEGIN"); + const OString aCommentStringPage("FIELD_SEQ_BEGIN;PageField"); + const OString aCommentStringEnd("FIELD_SEQ_END"); + OUString aURL; + + switch (rFieldPrimitive.getType()) + { + default: // case drawinglayer::primitive2d::FIELD_TYPE_COMMON : + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon)); + break; } - - void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive) + case drawinglayer::primitive2d::FIELD_TYPE_PAGE: + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage)); + break; + } + case drawinglayer::primitive2d::FIELD_TYPE_URL: { - const OString aCommentString("XTEXT_EOP"); - static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore + aURL = rFieldPrimitive.getValue("URL"); - if(nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport) + if (!aURL.isEmpty()) { - // Non-PDF export behaviour (metafile only). - // Process recursively and add MetaFile comment. - process(rParagraphPrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); - return; + mpMetaFile->AddAction(new MetaCommentAction( + aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()), + 2 * aURL.getLength())); } - if(!mpPDFExtOutDevData->GetIsExportTaggedPDF()) - { - // No Tagged PDF -> Dump as Paragraph - // Emulate data handling from old ImpEditEngine::Paint - mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph ); + break; + } + } - // Process recursively and add MetaFile comment - process(rParagraphPrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + // process recursively + primitive2d::Primitive2DContainer rContent; + rFieldPrimitive.get2DDecomposition(rContent, 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())) + return; + + // emulate data handling from ImpEditEngine::Paint + const basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D())); + const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())), + static_cast<sal_Int32>(floor(aViewRange.getMinY())), + static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), + static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); + vcl::PDFExtOutDevBookmarkEntry aBookmark; + aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic); + aBookmark.aBookmark = aURL; + std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = mpPDFExtOutDevData->GetBookmarks(); + rBookmarks.push_back(aBookmark); +} + +void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D( + const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive) +{ + const OString aCommentString("XTEXT_EOL"); - // Emulate data handling from ImpEditEngine::Paint - mpPDFExtOutDevData->EndStructureElement(); - return; - } + // process recursively and add MetaFile comment + process(rLinePrimitive); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); +} - // Create Tagged PDF -> deeper tagged data using StructureElements. - // Use OutlineLevel from ParagraphPrimitive, ensure not below -1 what - // means 'not active' - const sal_Int16 nNewOutlineLevel(std::max(static_cast<sal_Int16>(-1), rParagraphPrimitive.getOutlineLevel())); +void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D( + const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive) +{ + // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The + // "XTEXT_EOC" is used, use here, too. + const OString aCommentString("XTEXT_EOC"); - // Do we have a change in OutlineLevel compared to the current one? - if(nNewOutlineLevel != mnCurrentOutlineLevel) - { - if(nNewOutlineLevel > mnCurrentOutlineLevel) - { - // increase List level - for(sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a) - { - maListElements.push(vcl::PDFWriter::List); - mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::List ); - } - } - else // if(nNewOutlineLevel < mnCurrentOutlineLevel) - { - // close list levels below nNewOutlineLevel completely by removing - // list items as well as list tag itself - for(sal_Int16 a(nNewOutlineLevel); a < mnCurrentOutlineLevel; ++a) - { - popList(); // end LBody LI and L - } + // this is a part of list item, start LILabel ( = bullet) + if (mbInListItem) + { + maListElements.push(vcl::PDFWriter::LILabel); + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel); + } - // on nNewOutlineLevel close the previous list item (LBody and LI) - popListItem(); + // process recursively and add MetaFile comment + process(rBulletPrimitive); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); - } + if (mbInListItem) + { + if (maListElements.top() == vcl::PDFWriter::LILabel) + { + maListElements.pop(); + mpPDFExtOutDevData->EndStructureElement(); // end LILabel + mbBulletPresent = true; + } + } +} - // Remember new current OutlineLevel - mnCurrentOutlineLevel = nNewOutlineLevel; - } - else // the same list level - { - // close the previous list item (LBody and LI) - popListItem(); - } +void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D( + const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive) +{ + const OString aCommentString("XTEXT_EOP"); + static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore + + if (nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport) + { + // Non-PDF export behaviour (metafile only). + // Process recursively and add MetaFile comment. + process(rParagraphPrimitive); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + return; + } + + if (!mpPDFExtOutDevData->GetIsExportTaggedPDF()) + { + // No Tagged PDF -> Dump as Paragraph + // Emulate data handling from old ImpEditEngine::Paint + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph); + + // Process recursively and add MetaFile comment + process(rParagraphPrimitive); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + // Emulate data handling from ImpEditEngine::Paint + mpPDFExtOutDevData->EndStructureElement(); + return; + } - const bool bDumpAsListItem(-1 != mnCurrentOutlineLevel); + // Create Tagged PDF -> deeper tagged data using StructureElements. + // Use OutlineLevel from ParagraphPrimitive, ensure not below -1 what + // means 'not active' + const sal_Int16 nNewOutlineLevel( + std::max(static_cast<sal_Int16>(-1), rParagraphPrimitive.getOutlineLevel())); - if(bDumpAsListItem) + // Do we have a change in OutlineLevel compared to the current one? + if (nNewOutlineLevel != mnCurrentOutlineLevel) + { + if (nNewOutlineLevel > mnCurrentOutlineLevel) + { + // increase List level + for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a) { - // Dump as ListItem - maListElements.push(vcl::PDFWriter::ListItem); - mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::ListItem ); - mbInListItem = true; + maListElements.push(vcl::PDFWriter::List); + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::List); } - else + } + else // if(nNewOutlineLevel < mnCurrentOutlineLevel) + { + // close list levels below nNewOutlineLevel completely by removing + // list items as well as list tag itself + for (sal_Int16 a(nNewOutlineLevel); a < mnCurrentOutlineLevel; ++a) { - // Dump as Paragraph - mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph ); + popList(); // end LBody LI and L } - // Process recursively and add MetaFile comment - process(rParagraphPrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); - - if(bDumpAsListItem) - mbInListItem = false; - else - mpPDFExtOutDevData->EndStructureElement(); // end Paragraph + // on nNewOutlineLevel close the previous list item (LBody and LI) + popListItem(); } - void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D(const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive) - { - const OString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN"); - const OString aCommentStringB("XTEXT_PAINTSHAPE_END"); + // Remember new current OutlineLevel + mnCurrentOutlineLevel = nNewOutlineLevel; + } + else // the same list level + { + // close the previous list item (LBody and LI) + popListItem(); + } - // add MetaFile comment, process recursively and add MetaFile comment - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA)); - process(rBlockPrimitive); + const bool bDumpAsListItem(-1 != mnCurrentOutlineLevel); - if (mnCurrentOutlineLevel >= 0 ) - { - // end any opened List structure elements (LBody, LI, L) - for(sal_Int16 a(0); a <= mnCurrentOutlineLevel; ++a) - { - popList(); - } - } + if (bDumpAsListItem) + { + // Dump as ListItem + maListElements.push(vcl::PDFWriter::ListItem); + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::ListItem); + mbInListItem = true; + } + else + { + // Dump as Paragraph + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph); + } - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB)); - } + // Process recursively and add MetaFile comment + process(rParagraphPrimitive); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + if (bDumpAsListItem) + mbInListItem = false; + else + mpPDFExtOutDevData->EndStructureElement(); // end Paragraph +} + +void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D( + const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive) +{ + const OString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN"); + const OString aCommentStringB("XTEXT_PAINTSHAPE_END"); - void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate) + // add MetaFile comment, process recursively and add MetaFile comment + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA)); + process(rBlockPrimitive); + + if (mnCurrentOutlineLevel >= 0) + { + // end any opened List structure elements (LBody, LI, L) + for (sal_Int16 a(0); a <= mnCurrentOutlineLevel; ++a) { - // Adapt evtl. used special DrawMode - const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); - adaptTextToFillDrawMode(); + popList(); + } + } - // this is a 2nd portion of list item - // bullet has been already processed, start LIBody - if (mbInListItem && mbBulletPresent) - { - maListElements.push(vcl::PDFWriter::LIBody); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody); - } + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB)); +} - // directdraw of text simple portion; use default processing - RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate); +void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate) +{ + // Adapt evtl. used special DrawMode + const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); - if (mbInListItem && mbBulletPresent) - mbBulletPresent = false; + // this is a 2nd portion of list item + // bullet has been already processed, start LIBody + if (mbInListItem && mbBulletPresent) + { + maListElements.push(vcl::PDFWriter::LIBody); + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody); + } - // restore DrawMode - mpOutputDevice->SetDrawMode(nOriginalDrawMode); + // directdraw of text simple portion; use default processing + RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate); - // #i101169# if(pTextDecoratedCandidate) - { - // support for TEXT_ MetaFile actions only for decorated texts - if(!mxBreakIterator.is()) - { - uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); - mxBreakIterator = i18n::BreakIterator::create(xContext); - } + if (mbInListItem && mbBulletPresent) + mbBulletPresent = false; - const OUString& rTxt = rTextCandidate.getText(); - const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength()); + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); - if(nTextLength) - { - const css::lang::Locale& rLocale = rTextCandidate.getLocale(); - const sal_Int32 nTextPosition(rTextCandidate.getTextPosition()); - - sal_Int32 nDone; - sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); - css::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true)); - sal_Int32 nNextSentenceBreak(mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); - const OString aCommentStringA("XTEXT_EOC"); - const OString aCommentStringB("XTEXT_EOW"); - const OString 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, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); - } - if(i == nNextWordBoundary.endPos) - { - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB, i - nTextPosition)); - nNextWordBoundary = mxBreakIterator->getWordBoundary(rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true); - } - if(i == nNextSentenceBreak) - { - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringC, i - nTextPosition)); - nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale); - } - } - } - } + // #i101169# if(pTextDecoratedCandidate) + { + // support for TEXT_ MetaFile actions only for decorated texts + if (!mxBreakIterator.is()) + { + uno::Reference<uno::XComponentContext> xContext( + ::comphelper::getProcessComponentContext()); + mxBreakIterator = i18n::BreakIterator::create(xContext); } - void VclMetafileProcessor2D::processPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive) - { - const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon(); + const OUString& rTxt = rTextCandidate.getText(); + const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength()); - 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); - rtl::Reference< primitive2d::PolygonHairlinePrimitive2D > xPLeft(new primitive2d::PolygonHairlinePrimitive2D(aLeft, rHairlinePrimitive.getBColor())); - rtl::Reference< primitive2d::PolygonHairlinePrimitive2D > xPRight(new primitive2d::PolygonHairlinePrimitive2D(aRight, rHairlinePrimitive.getBColor())); - - processBasePrimitive2D(*xPLeft); - processBasePrimitive2D(*xPRight); - } - else + if (nTextLength) + { + const css::lang::Locale& rLocale = rTextCandidate.getLocale(); + const sal_Int32 nTextPosition(rTextCandidate.getTextPosition()); + + sal_Int32 nDone; + sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters( + rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0, + nDone)); + css::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary( + rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true)); + sal_Int32 nNextSentenceBreak( + mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); + const OString aCommentStringA("XTEXT_EOC"); + const OString aCommentStringB("XTEXT_EOW"); + const OString aCommentStringC("XTEXT_EOS"); + + for (sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++) { - // direct draw of hairline; use default processing - // support SvtGraphicStroke MetaCommentAction - const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor())); - std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke; - - // #i121267# Not needed, does not give better quality compared with - // the MetaActionType::POLYPOLYGON written by RenderPolygonHairlinePrimitive2D - // below - const bool bSupportSvtGraphicStroke(false); - - if(bSupportSvtGraphicStroke) + // create the entries for the respective break positions + if (i == nNextCellBreak) { - pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( - rHairlinePrimitive.getB2DPolygon(), - &aLineColor, - nullptr, nullptr, nullptr, nullptr); - - impStartSvtGraphicStroke(pSvtGraphicStroke.get()); + mpMetaFile->AddAction( + new MetaCommentAction(aCommentStringA, i - nTextPosition)); + nNextCellBreak = mxBreakIterator->nextCharacters( + rTxt, i, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); } - - RenderPolygonHairlinePrimitive2D(rHairlinePrimitive, false); - - if(bSupportSvtGraphicStroke) + if (i == nNextWordBoundary.endPos) + { + mpMetaFile->AddAction( + new MetaCommentAction(aCommentStringB, i - nTextPosition)); + nNextWordBoundary = mxBreakIterator->getWordBoundary( + rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true); + } + if (i == nNextSentenceBreak) { - impEndSvtGraphicStroke(pSvtGraphicStroke.get()); + mpMetaFile->AddAction( + new MetaCommentAction(aCommentStringC, i - nTextPosition)); + nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale); } } } + } +} + +void VclMetafileProcessor2D::processPolygonHairlinePrimitive2D( + const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive) +{ + const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon(); - void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive) + 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); + rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPLeft( + new primitive2d::PolygonHairlinePrimitive2D(aLeft, rHairlinePrimitive.getBColor())); + rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPRight( + new primitive2d::PolygonHairlinePrimitive2D(aRight, rHairlinePrimitive.getBColor())); + + processBasePrimitive2D(*xPLeft); + processBasePrimitive2D(*xPRight); + } + else + { + // direct draw of hairline; use default processing + // support SvtGraphicStroke MetaCommentAction + const basegfx::BColor aLineColor( + maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor())); + std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke; + + // #i121267# Not needed, does not give better quality compared with + // the MetaActionType::POLYPOLYGON written by RenderPolygonHairlinePrimitive2D + // below + const bool bSupportSvtGraphicStroke(false); + + if (bSupportSvtGraphicStroke) { - const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon(); + pSvtGraphicStroke + = impTryToCreateSvtGraphicStroke(rHairlinePrimitive.getB2DPolygon(), &aLineColor, + nullptr, nullptr, nullptr, nullptr); - 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); - rtl::Reference< primitive2d::PolygonStrokePrimitive2D > xPLeft(new primitive2d::PolygonStrokePrimitive2D( - aLeft, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute())); - rtl::Reference< primitive2d::PolygonStrokePrimitive2D > xPRight(new primitive2d::PolygonStrokePrimitive2D( - aRight, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute())); - - processBasePrimitive2D(*xPLeft); - processBasePrimitive2D(*xPRight); - } - else - { - mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + impStartSvtGraphicStroke(pSvtGraphicStroke.get()); + } - // support SvtGraphicStroke MetaCommentAction - std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( - rBasePolygon, nullptr, - &rStrokePrimitive.getLineAttribute(), - &rStrokePrimitive.getStrokeAttribute(), - nullptr, nullptr); + RenderPolygonHairlinePrimitive2D(rHairlinePrimitive, false); - impStartSvtGraphicStroke(pSvtGraphicStroke.get()); - const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute(); + if (bSupportSvtGraphicStroke) + { + impEndSvtGraphicStroke(pSvtGraphicStroke.get()); + } + } +} - // create MetaPolyLineActions, but without LineStyle::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::utils::applyLineDashing( - rBasePolygon, rStroke.getDotDashArray(), - &aHairLinePolyPolygon, nullptr, rStroke.getFullDotDashLen()); - } - - const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor())); - mpOutputDevice->SetLineColor(Color(aHairlineColor)); - mpOutputDevice->SetFillColor(); - aHairLinePolyPolygon.transform(maCurrentTransformation); - - // use the transformed line width - LineInfo aLineInfo(LineStyle::Solid, basegfx::fround(getTransformedLineWidth(rLine.getWidth()))); - aLineInfo.SetLineJoin(rLine.getLineJoin()); - aLineInfo.SetLineCap(rLine.getLineCap()); - - for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) - { - const basegfx::B2DPolygon& aCandidate(aHairLinePolyPolygon.getB2DPolygon(a)); - - if(aCandidate.count() > 1) - { - const ::tools::Polygon aToolsPolygon(aCandidate); - - mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo)); - } - } - } - else - { - process(rStrokePrimitive); - } +void VclMetafileProcessor2D::processPolygonStrokePrimitive2D( + const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive) +{ + const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon(); - impEndSvtGraphicStroke(pSvtGraphicStroke.get()); + 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); + rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPLeft( + new primitive2d::PolygonStrokePrimitive2D(aLeft, rStrokePrimitive.getLineAttribute(), + rStrokePrimitive.getStrokeAttribute())); + rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPRight( + new primitive2d::PolygonStrokePrimitive2D(aRight, rStrokePrimitive.getLineAttribute(), + rStrokePrimitive.getStrokeAttribute())); + + processBasePrimitive2D(*xPLeft); + processBasePrimitive2D(*xPRight); + } + else + { + mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); - mpOutputDevice->Pop(); - } - } + // support SvtGraphicStroke MetaCommentAction + std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rBasePolygon, nullptr, &rStrokePrimitive.getLineAttribute(), + &rStrokePrimitive.getStrokeAttribute(), nullptr, nullptr); - void VclMetafileProcessor2D::processPolygonStrokeArrowPrimitive2D(const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive) + impStartSvtGraphicStroke(pSvtGraphicStroke.get()); + const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute(); + + // create MetaPolyLineActions, but without LineStyle::Dash + if (basegfx::fTools::more(rLine.getWidth(), 0.0)) { - const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon(); + const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute(); + basegfx::B2DPolyPolygon aHairLinePolyPolygon; - if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + if (0.0 == rStroke.getFullDotDashLen()) { - // #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; - rtl::Reference< primitive2d::PolygonStrokeArrowPrimitive2D > xPLeft(new primitive2d::PolygonStrokeArrowPrimitive2D( - aLeft, - rStrokeArrowPrimitive.getLineAttribute(), - rStrokeArrowPrimitive.getStrokeAttribute(), - rStrokeArrowPrimitive.getStart(), - aEmpty)); - rtl::Reference< primitive2d::PolygonStrokeArrowPrimitive2D > xPRight(new primitive2d::PolygonStrokeArrowPrimitive2D( - aRight, - rStrokeArrowPrimitive.getLineAttribute(), - rStrokeArrowPrimitive.getStrokeAttribute(), - aEmpty, - rStrokeArrowPrimitive.getEnd())); - - processBasePrimitive2D(*xPLeft); - processBasePrimitive2D(*xPRight); + aHairLinePolyPolygon.append(rBasePolygon); } else { - // support SvtGraphicStroke MetaCommentAction - std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( - rBasePolygon, nullptr, - &rStrokeArrowPrimitive.getLineAttribute(), - &rStrokeArrowPrimitive.getStrokeAttribute(), - &rStrokeArrowPrimitive.getStart(), - &rStrokeArrowPrimitive.getEnd()); - - // write LineGeometry start marker - impStartSvtGraphicStroke(pSvtGraphicStroke.get()); - - // #i116162# When B&W is set as DrawMode, DrawModeFlags::WhiteFill is used - // to let all fills be just white; for lines DrawModeFlags::BlackLine is used - // so all line geometry is supposed to get black. Since in the in-between - // stages of line geometry drawing filled polygons are used (e.g. line - // start/ends) it is necessary to change these drawmodes to preserve - // that lines shall be black; thus change DrawModeFlags::WhiteFill to - // DrawModeFlags::BlackFill during line geometry processing to have line geometry - // parts filled black. - const DrawModeFlags nOldDrawMode(mpOutputDevice->GetDrawMode()); - const bool bDrawmodeChange(nOldDrawMode & DrawModeFlags::WhiteFill && mnSvtGraphicStrokeCount); - - if(bDrawmodeChange) - { - mpOutputDevice->SetDrawMode((nOldDrawMode & ~DrawModeFlags::WhiteFill) | DrawModeFlags::BlackFill); - } + basegfx::utils::applyLineDashing(rBasePolygon, rStroke.getDotDashArray(), + &aHairLinePolyPolygon, nullptr, + rStroke.getFullDotDashLen()); + } + + const basegfx::BColor aHairlineColor( + maBColorModifierStack.getModifiedColor(rLine.getColor())); + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + aHairLinePolyPolygon.transform(maCurrentTransformation); - // process sub-line geometry (evtl. filled PolyPolygons) - process(rStrokeArrowPrimitive); + // use the transformed line width + LineInfo aLineInfo(LineStyle::Solid, + basegfx::fround(getTransformedLineWidth(rLine.getWidth()))); + aLineInfo.SetLineJoin(rLine.getLineJoin()); + aLineInfo.SetLineCap(rLine.getLineCap()); - if(bDrawmodeChange) + for (sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) + { + const basegfx::B2DPolygon& aCandidate(aHairLinePolyPolygon.getB2DPolygon(a)); + + if (aCandidate.count() > 1) { - mpOutputDevice->SetDrawMode(nOldDrawMode); - } + const tools::Polygon aToolsPolygon(aCandidate); - // write LineGeometry end marker - impEndSvtGraphicStroke(pSvtGraphicStroke.get()); + mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo)); + } } } - - void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate) + else { - // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END - basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon()); - - fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); + process(rStrokePrimitive); + } - std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; + impEndSvtGraphicStroke(pSvtGraphicStroke.get()); - if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) - { - // #121194# Changed implementation and checked usages of convert to metafile, - // presentation start (uses SvtGraphicFill) and printing. + mpOutputDevice->Pop(); + } +} - // calculate transformation. Get real object size, all values in FillGraphicAttribute - // are relative to the unified object - aLocalPolyPolygon.transform(maCurrentTransformation); - const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange()); - - // the scaling needs scale from pixel to logic coordinate system - const attribute::FillGraphicAttribute& rFillGraphicAttribute = rBitmapCandidate.getFillGraphic(); - const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel()); - - // setup transformation like in impgrfll. Multiply with aOutlineSize - // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange() - // to object coordinates with object's top left being at (0,0). Divide - // by pixel size so that scale from pixel to logic will work in SvtGraphicFill. - const basegfx::B2DVector aTransformScale( - rFillGraphicAttribute.getGraphicRange().getRange() / - basegfx::B2DVector( - std::max(1.0, double(aBmpSizePixel.Width())), - std::max(1.0, double(aBmpSizePixel.Height()))) * - aOutlineSize); - const basegfx::B2DPoint aTransformPosition( - rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize); - - // setup transformation like in impgrfll - SvtGraphicFill::Transform aTransform; - - // scale values are divided by bitmap pixel sizes - aTransform.matrix[0] = aTransformScale.getX(); - aTransform.matrix[4] = aTransformScale.getY(); - - // translates are absolute - aTransform.matrix[2] = aTransformPosition.getX(); - aTransform.matrix[5] = aTransformPosition.getY(); - - pSvtGraphicFill.reset( new SvtGraphicFill( - getFillPolyPolygon(aLocalPolyPolygon), - Color(), - 0.0, - SvtGraphicFill::fillEvenOdd, - SvtGraphicFill::fillTexture, - aTransform, - rFillGraphicAttribute.getTiling(), - SvtGraphicFill::hatchSingle, - Color(), - SvtGraphicFill::GradientType::Linear, - Color(), - Color(), - 0, - rFillGraphicAttribute.getGraphic()) ); - } +void VclMetafileProcessor2D::processPolygonStrokeArrowPrimitive2D( + const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive) +{ + const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon(); - // Do use decomposition; encapsulate with SvtGraphicFill - impStartSvtGraphicFill(pSvtGraphicFill.get()); - process(rBitmapCandidate); - impEndSvtGraphicFill(pSvtGraphicFill.get()); + 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; + rtl::Reference<primitive2d::PolygonStrokeArrowPrimitive2D> xPLeft( + new primitive2d::PolygonStrokeArrowPrimitive2D( + aLeft, rStrokeArrowPrimitive.getLineAttribute(), + rStrokeArrowPrimitive.getStrokeAttribute(), rStrokeArrowPrimitive.getStart(), + aEmpty)); + rtl::Reference<primitive2d::PolygonStrokeArrowPrimitive2D> xPRight( + new primitive2d::PolygonStrokeArrowPrimitive2D( + aRight, rStrokeArrowPrimitive.getLineAttribute(), + rStrokeArrowPrimitive.getStrokeAttribute(), aEmpty, + rStrokeArrowPrimitive.getEnd())); + + processBasePrimitive2D(*xPLeft); + processBasePrimitive2D(*xPRight); + } + else + { + // support SvtGraphicStroke MetaCommentAction + std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rBasePolygon, nullptr, &rStrokeArrowPrimitive.getLineAttribute(), + &rStrokeArrowPrimitive.getStrokeAttribute(), &rStrokeArrowPrimitive.getStart(), + &rStrokeArrowPrimitive.getEnd()); + + // write LineGeometry start marker + impStartSvtGraphicStroke(pSvtGraphicStroke.get()); + + // #i116162# When B&W is set as DrawMode, DrawModeFlags::WhiteFill is used + // to let all fills be just white; for lines DrawModeFlags::BlackLine is used + // so all line geometry is supposed to get black. Since in the in-between + // stages of line geometry drawing filled polygons are used (e.g. line + // start/ends) it is necessary to change these drawmodes to preserve + // that lines shall be black; thus change DrawModeFlags::WhiteFill to + // DrawModeFlags::BlackFill during line geometry processing to have line geometry + // parts filled black. + const DrawModeFlags nOldDrawMode(mpOutputDevice->GetDrawMode()); + const bool bDrawmodeChange(nOldDrawMode & DrawModeFlags::WhiteFill + && mnSvtGraphicStrokeCount); + + if (bDrawmodeChange) + { + mpOutputDevice->SetDrawMode((nOldDrawMode & ~DrawModeFlags::WhiteFill) + | DrawModeFlags::BlackFill); } - void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate) - { - // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END - const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch(); - basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon()); + // process sub-line geometry (evtl. filled PolyPolygons) + process(rStrokeArrowPrimitive); - if(aLocalPolyPolygon.getB2DRange() != rHatchCandidate.getDefinitionRange()) - { - // the range which defines the hatch is different from the range of the - // geometry (used for writer frames). This cannot be done calling vcl, thus use - // decomposition here - process(rHatchCandidate); - return; - } + if (bDrawmodeChange) + { + mpOutputDevice->SetDrawMode(nOldDrawMode); + } - // #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 - fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); + // write LineGeometry end marker + impEndSvtGraphicStroke(pSvtGraphicStroke.get()); + } +} - 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::Primitive2DContainer { xBackground }); - } +void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D( + const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate) +{ + // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END + basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon()); - std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; - aLocalPolyPolygon.transform(maCurrentTransformation); + fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); - if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) - { - // re-create a VCL hatch as base data - SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle); + std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; - 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; - } - } + if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // #121194# Changed implementation and checked usages of convert to metafile, + // presentation start (uses SvtGraphicFill) and printing. + + // calculate transformation. Get real object size, all values in FillGraphicAttribute + // are relative to the unified object + aLocalPolyPolygon.transform(maCurrentTransformation); + const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange()); + + // the scaling needs scale from pixel to logic coordinate system + const attribute::FillGraphicAttribute& rFillGraphicAttribute + = rBitmapCandidate.getFillGraphic(); + const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel()); + + // setup transformation like in impgrfll. Multiply with aOutlineSize + // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange() + // to object coordinates with object's top left being at (0,0). Divide + // by pixel size so that scale from pixel to logic will work in SvtGraphicFill. + const basegfx::B2DVector aTransformScale( + rFillGraphicAttribute.getGraphicRange().getRange() + / basegfx::B2DVector(std::max(1.0, double(aBmpSizePixel.Width())), + std::max(1.0, double(aBmpSizePixel.Height()))) + * aOutlineSize); + const basegfx::B2DPoint aTransformPosition( + rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize); + + // setup transformation like in impgrfll + SvtGraphicFill::Transform aTransform; + + // scale values are divided by bitmap pixel sizes + aTransform.matrix[0] = aTransformScale.getX(); + aTransform.matrix[4] = aTransformScale.getY(); + + // translates are absolute + aTransform.matrix[2] = aTransformPosition.getX(); + aTransform.matrix[5] = aTransformPosition.getY(); + + pSvtGraphicFill.reset(new SvtGraphicFill( + getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillTexture, aTransform, rFillGraphicAttribute.getTiling(), + SvtGraphicFill::hatchSingle, Color(), SvtGraphicFill::GradientType::Linear, Color(), + Color(), 0, rFillGraphicAttribute.getGraphic())); + } - 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.reset( new SvtGraphicFill( - getFillPolyPolygon(aLocalPolyPolygon), - Color(), - 0.0, - SvtGraphicFill::fillEvenOdd, - SvtGraphicFill::fillHatch, - aTransform, - false, - eHatch, - Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())), - SvtGraphicFill::GradientType::Linear, - Color(), - Color(), - 0, - Graphic()) ); - } + // Do use decomposition; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill.get()); + process(rBitmapCandidate); + impEndSvtGraphicFill(pSvtGraphicFill.get()); +} - // Do use decomposition; encapsulate with SvtGraphicFill - impStartSvtGraphicFill(pSvtGraphicFill.get()); +void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D( + const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate) +{ + // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END + const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch(); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon()); - // #i111954# do NOT use decomposition, but use direct VCL-command - // process(rCandidate.get2DDecomposition(getViewInformation2D())); - const ::tools::PolyPolygon aToolsPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)); - const HatchStyle aHatchStyle( - attribute::HatchStyle::Single == rFillHatchAttribute.getStyle() ? HatchStyle::Single : - attribute::HatchStyle::Double == rFillHatchAttribute.getStyle() ? HatchStyle::Double : - HatchStyle::Triple); + if (aLocalPolyPolygon.getB2DRange() != rHatchCandidate.getDefinitionRange()) + { + // the range which defines the hatch is different from the range of the + // geometry (used for writer frames). This cannot be done calling vcl, thus use + // decomposition here + process(rHatchCandidate); + return; + } - mpOutputDevice->DrawHatch(aToolsPolyPolygon, - Hatch(aHatchStyle, - Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())), - basegfx::fround(rFillHatchAttribute.getDistance()), - basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800))); + // #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 + fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); - impEndSvtGraphicFill(pSvtGraphicFill.get()); - } + 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::Primitive2DContainer{ xBackground }); + } - void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate) - { - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; + std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; + aLocalPolyPolygon.transform(maCurrentTransformation); - maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // re-create a VCL hatch as base data + SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle); - if(!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) + switch (rFillHatchAttribute.getStyle()) + { + default: // attribute::HatchStyle::Single : { - // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly. - // This is because VCL Gradient mechanism does *not* support to rotate the gradient - // with objects and this case is not expressible in a Metafile (and cannot be added - // since the FileFormats used, e.g. *.wmf, do not support it either). - // Such cases happen when a graphic object uses a Metafile as graphic information or - // a fill style definition uses a Metafile. In this cases the graphic content is - // rotated with the graphic or filled object; this is not supported by the target - // format of this conversion renderer - Metafiles. - // To solve this, not a Gradient is written, but the decomposition of this object - // is written to the Metafile. This is the PolyPolygons building the gradient fill. - // These will need more space and time, but the result will be as if the Gradient - // was rotated with the object. - // This mechanism is used by all exporters still not using Primitives (e.g. Print, - // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile - // transfers. One more reason to *change* these to primitives. - // BTW: One more example how useful the principles of primitives are; the decomposition - // is by definition a simpler, maybe more expensive representation of the same content. - process(rGradientCandidate); - return; + eHatch = SvtGraphicFill::hatchSingle; + break; } - - basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon()); - - if(aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange()) + case attribute::HatchStyle::Double: { - // the range which defines the gradient is different from the range of the - // geometry (used for writer frames). This cannot be done calling vcl, thus use - // decomposition here - process(rGradientCandidate); - return; + eHatch = SvtGraphicFill::hatchDouble; + break; } + case attribute::HatchStyle::Triple: + { + eHatch = SvtGraphicFill::hatchTriple; + break; + } + } - // #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 - fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); + SvtGraphicFill::Transform aTransform; - // 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); + // scale + aTransform.matrix[0] *= rFillHatchAttribute.getDistance(); + aTransform.matrix[4] *= rFillHatchAttribute.getDistance(); - // #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 ::tools::PolyPolygon aToolsPolyPolygon( - getFillPolyPolygon( - basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon))); + // 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.reset(new SvtGraphicFill( + getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillHatch, aTransform, false, eHatch, + Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())), + SvtGraphicFill::GradientType::Linear, Color(), Color(), 0, Graphic())); + } + // Do use decomposition; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill.get()); + + // #i111954# do NOT use decomposition, but use direct VCL-command + // process(rCandidate.get2DDecomposition(getViewInformation2D())); + const tools::PolyPolygon aToolsPolyPolygon( + basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)); + const HatchStyle aHatchStyle( + attribute::HatchStyle::Single == rFillHatchAttribute.getStyle() + ? HatchStyle::Single + : attribute::HatchStyle::Double == rFillHatchAttribute.getStyle() ? HatchStyle::Double + : HatchStyle::Triple); + + mpOutputDevice->DrawHatch( + aToolsPolyPolygon, + Hatch(aHatchStyle, + Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())), + basegfx::fround(rFillHatchAttribute.getDistance()), + basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800))); + + impEndSvtGraphicFill(pSvtGraphicFill.get()); +} + +void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( + const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate) +{ + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; - // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support - std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; + maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); - if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) - { - // setup gradient stuff like in impgrfll - SvtGraphicFill::GradientType eGrad(SvtGraphicFill::GradientType::Linear); + if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) + { + // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly. + // This is because VCL Gradient mechanism does *not* support to rotate the gradient + // with objects and this case is not expressible in a Metafile (and cannot be added + // since the FileFormats used, e.g. *.wmf, do not support it either). + // Such cases happen when a graphic object uses a Metafile as graphic information or + // a fill style definition uses a Metafile. In this cases the graphic content is + // rotated with the graphic or filled object; this is not supported by the target + // format of this conversion renderer - Metafiles. + // To solve this, not a Gradient is written, but the decomposition of this object + // is written to the Metafile. This is the PolyPolygons building the gradient fill. + // These will need more space and time, but the result will be as if the Gradient + // was rotated with the object. + // This mechanism is used by all exporters still not using Primitives (e.g. Print, + // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile + // transfers. One more reason to *change* these to primitives. + // BTW: One more example how useful the principles of primitives are; the decomposition + // is by definition a simpler, maybe more expensive representation of the same content. + process(rGradientCandidate); + return; + } - switch(aVCLGradient.GetStyle()) - { - default : // GradientStyle::Linear: - case GradientStyle::Axial: - eGrad = SvtGraphicFill::GradientType::Linear; - break; - case GradientStyle::Radial: - case GradientStyle::Elliptical: - eGrad = SvtGraphicFill::GradientType::Radial; - break; - case GradientStyle::Square: - case GradientStyle::Rect: - eGrad = SvtGraphicFill::GradientType::Rectangular; - break; - } + basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon()); - pSvtGraphicFill.reset( new SvtGraphicFill( - aToolsPolyPolygon, - Color(), - 0.0, - SvtGraphicFill::fillEvenOdd, - SvtGraphicFill::fillGradient, - SvtGraphicFill::Transform(), - false, - SvtGraphicFill::hatchSingle, - Color(), - eGrad, - aVCLGradient.GetStartColor(), - aVCLGradient.GetEndColor(), - aVCLGradient.GetSteps(), - Graphic()) ); - } + if (aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange()) + { + // the range which defines the gradient is different from the range of the + // geometry (used for writer frames). This cannot be done calling vcl, thus use + // decomposition here + process(rGradientCandidate); + return; + } - // call VCL directly; encapsulate with SvtGraphicFill - impStartSvtGraphicFill(pSvtGraphicFill.get()); - mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); - impEndSvtGraphicFill(pSvtGraphicFill.get()); - } + // #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 + 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 tools::PolyPolygon aToolsPolyPolygon( + getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon))); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; + + if (!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup gradient stuff like in impgrfll + SvtGraphicFill::GradientType eGrad(SvtGraphicFill::GradientType::Linear); - void VclMetafileProcessor2D::processPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate) + switch (aVCLGradient.GetStyle()) { - mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); - basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + default: // GradientStyle::Linear: + case GradientStyle::Axial: + eGrad = SvtGraphicFill::GradientType::Linear; + break; + case GradientStyle::Radial: + case GradientStyle::Elliptical: + eGrad = SvtGraphicFill::GradientType::Radial; + break; + case GradientStyle::Square: + case GradientStyle::Rect: + eGrad = SvtGraphicFill::GradientType::Rectangular; + break; + } - // #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 - fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); + pSvtGraphicFill.reset(new SvtGraphicFill( + aToolsPolyPolygon, Color(), 0.0, SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillGradient, SvtGraphicFill::Transform(), false, + SvtGraphicFill::hatchSingle, Color(), eGrad, aVCLGradient.GetStartColor(), + aVCLGradient.GetEndColor(), aVCLGradient.GetSteps(), Graphic())); + } - const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); - aLocalPolyPolygon.transform(maCurrentTransformation); + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill.get()); + mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); + impEndSvtGraphicFill(pSvtGraphicFill.get()); +} - // set line and fill color - mpOutputDevice->SetFillColor(Color(aPolygonColor)); - mpOutputDevice->SetLineColor(); +void VclMetafileProcessor2D::processPolyPolygonColorPrimitive2D( + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate) +{ + mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); - mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + // #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 + fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); - mpOutputDevice->Pop(); - } + const basegfx::BColor aPolygonColor( + maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + aLocalPolyPolygon.transform(maCurrentTransformation); - void VclMetafileProcessor2D::processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate) - { - // mask group. Special handling for MetaFiles. - if(rMaskCandidate.getChildren().empty()) - return; + // set line and fill color + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); - basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); - if(aMask.count()) - { - // prepare new mask polygon and rescue current one - aMask.transform(maCurrentTransformation); - const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon); + mpOutputDevice->Pop(); +} - if(maClipPolyPolygon.count()) - { - // there is already a clip polygon set; build clipped union of - // current mask polygon and new one - maClipPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( - aMask, - maClipPolyPolygon, - true, // #i106516# we want the inside of aMask, not the outside - false); - } - else - { - // use mask directly - maClipPolyPolygon = aMask; - } +void VclMetafileProcessor2D::processMaskPrimitive2D( + const primitive2d::MaskPrimitive2D& rMaskCandidate) +{ + // mask group. Special handling for MetaFiles. + if (rMaskCandidate.getChildren().empty()) + return; - if(maClipPolyPolygon.count()) - { - // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!) - // Removed subdivision and fixed in vcl::Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where - // the ClipRegion is built from the Polygon. An AdaptiveSubdivide on the source polygon was missing there - mpOutputDevice->Push(PushFlags::CLIPREGION); - mpOutputDevice->SetClipRegion(vcl::Region(maClipPolyPolygon)); - - // recursively paint content - // #i121267# Only need to process sub-content when clip polygon is *not* empty. - // If it is empty, the clip is empty and there can be nothing inside. - process(rMaskCandidate.getChildren()); - - // restore VCL clip region - mpOutputDevice->Pop(); - } + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); - // restore to rescued clip polygon - maClipPolyPolygon = aLastClipPolyPolygon; - } - else - { - // no mask, no clipping. recursively paint content - process(rMaskCandidate.getChildren()); - } - } + if (aMask.count()) + { + // prepare new mask polygon and rescue current one + aMask.transform(maCurrentTransformation); + const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon); - void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) + if (maClipPolyPolygon.count()) { - mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); - // 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 PolyPolygons directly. Can be detected by - // checking the content for single PolyPolygonColorPrimitive2D - const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren(); - - if(!rContent.empty()) - { - 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 = nullptr; - static bool bForceToMetafile(false); // loplugin:constvars:ignore - - if(!bForceToMetafile && 1 == rContent.size()) - { - const primitive2d::Primitive2DReference xReference(rContent[0]); - pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get()); - } - - // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and - // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D. - // Check also for correct ID to exclude derived implementations - if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID()) - { - // single transparent tools::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 - fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); - - // now transform - aLocalPolyPolygon.transform(maCurrentTransformation); - - // set line and fill color - const sal_uInt16 nTransPercentVcl(static_cast<sal_uInt16>(basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0))); - mpOutputDevice->SetFillColor(Color(aPolygonColor)); - mpOutputDevice->SetLineColor(); - - mpOutputDevice->DrawTransparent( - ::tools::PolyPolygon(aLocalPolyPolygon), - nTransPercentVcl); - } - else - { - // save 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 ::tools::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(static_cast<sal_uInt8>(basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0))); - const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl); - - aVCLGradient.SetStyle(GradientStyle::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); - } - } - } - - mpOutputDevice->Pop(); + // there is already a clip polygon set; build clipped union of + // current mask polygon and new one + maClipPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( + aMask, maClipPolyPolygon, + true, // #i106516# we want the inside of aMask, not the outside + false); + } + else + { + // use mask directly + maClipPolyPolygon = aMask; } - void VclMetafileProcessor2D::processTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransparenceCandidate) + if (maClipPolyPolygon.count()) { - // 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 a transparence-blended bitmap. Eventually - // do that in stripes, else RenderTransparencePrimitive2D may just be used - const primitive2d::Primitive2DContainer& rContent = rTransparenceCandidate.getChildren(); - const primitive2d::Primitive2DContainer& rTransparence = rTransparenceCandidate.getTransparence(); + // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!) + // Removed subdivision and fixed in vcl::Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where + // the ClipRegion is built from the Polygon. An AdaptiveSubdivide on the source polygon was missing there + mpOutputDevice->Push(PushFlags::CLIPREGION); + mpOutputDevice->SetClipRegion(vcl::Region(maClipPolyPolygon)); + + // recursively paint content + // #i121267# Only need to process sub-content when clip polygon is *not* empty. + // If it is empty, the clip is empty and there can be nothing inside. + process(rMaskCandidate.getChildren()); + + // restore VCL clip region + mpOutputDevice->Pop(); + } - if(rContent.empty() || rTransparence.empty()) - return; + // restore to rescued clip polygon + maClipPolyPolygon = aLastClipPolyPolygon; + } + else + { + // no mask, no clipping. recursively paint content + process(rMaskCandidate.getChildren()); + } +} - // try to identify a single FillGradientPrimitive2D in the - // transparence part of the primitive - const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr; - static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore +void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D( + const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) +{ + mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + // 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 PolyPolygons directly. Can be detected by + // checking the content for single PolyPolygonColorPrimitive2D + const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren(); + + if (!rContent.empty()) + { + 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 = nullptr; + static bool bForceToMetafile(false); // loplugin:constvars:ignore - if(!bForceToBigTransparentVDev && 1 == rTransparence.size()) + if (!bForceToMetafile && 1 == rContent.size()) { - const primitive2d::Primitive2DReference xReference(rTransparence[0]); - pFiGradient = dynamic_cast< const primitive2d::FillGradientPrimitive2D* >(xReference.get()); + const primitive2d::Primitive2DReference xReference(rContent[0]); + pPoPoColor = dynamic_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( + xReference.get()); } + // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and + // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D. // Check also for correct ID to exclude derived implementations - if(pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID()) + if (pPoPoColor + && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID()) { - // various content, create content-metafile - GDIMetaFile aContentMetafile; - const ::tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile)); + // single transparent tools::PolyPolygon identified, use directly + const basegfx::BColor aPolygonColor( + maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor())); + basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon()); - // re-create a VCL-gradient from FillGradientPrimitive2D - Gradient aVCLGradient; - impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true); + // #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 + fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); - // render it to VCL - mpOutputDevice->DrawTransparent( - aContentMetafile, aPrimitiveRectangle.TopLeft(), - aPrimitiveRectangle.GetSize(), aVCLGradient); + // now transform + aLocalPolyPolygon.transform(maCurrentTransformation); + + // set line and fill color + const sal_uInt16 nTransPercentVcl(static_cast<sal_uInt16>( + basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0))); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + mpOutputDevice->DrawTransparent(tools::PolyPolygon(aLocalPolyPolygon), + nTransPercentVcl); } 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 no one constructs - // transparence primitives with non-trivial transparence content) i will for now not - // refine to tiling here. - - basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D())); - aViewRange.transform(maCurrentTransformation); - const ::tools::Rectangle aRectLogic( - static_cast<sal_Int32>(floor(aViewRange.getMinX())), static_cast<sal_Int32>(floor(aViewRange.getMinY())), - static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); - const ::tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic)); - Size aSizePixel(aRectPixel.GetSize()); - const Point aEmptyPoint; - ScopedVclPtrInstance< VirtualDevice > aBufferDevice; - const sal_uInt32 nMaxQuadratPixels(500000); - const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight()); - double fReduceFactor(1.0); - - if(nViewVisibleArea > nMaxQuadratPixels) - { - // reduce render size - fReduceFactor = sqrt(double(nMaxQuadratPixels) / static_cast<double>(nViewVisibleArea)); - aSizePixel = Size(basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * fReduceFactor), - basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * fReduceFactor)); - } + // save 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; - 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 MapUnit::MapInch needs to be used. - basegfx::B2DHomMatrix aViewTransform(aBufferDevice->GetViewTransformation()); - const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); - const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); - const double fDPIXChange(static_cast<double>(aDPIOld.getWidth()) / static_cast<double>(aDPINew.getWidth())); - const double fDPIYChange(static_cast<double>(aDPIOld.getHeight()) / static_cast<double>(aDPINew.getHeight())); - - if(!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0)) - { - aViewTransform.scale(fDPIXChange, fDPIYChange); - } - - // also take scaling from Size reduction into account - if(!basegfx::fTools::equal(fReduceFactor, 1.0)) - { - aViewTransform.scale(fReduceFactor, fReduceFactor); - } - - // 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)); - - // paint - mpOutputDevice->DrawBitmapEx( - aRectLogic.TopLeft(), - aRectLogic.GetSize(), - BitmapEx(aBmContent, aBmAlpha)); - } + // various content, create content-metafile + GDIMetaFile aContentMetafile; + const tools::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(static_cast<sal_uInt8>( + basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0))); + const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl); + + aVCLGradient.SetStyle(GradientStyle::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); } } + } + + mpOutputDevice->Pop(); +} + +void VclMetafileProcessor2D::processTransparencePrimitive2D( + const primitive2d::TransparencePrimitive2D& rTransparenceCandidate) +{ + // 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 a transparence-blended bitmap. Eventually + // do that in stripes, else RenderTransparencePrimitive2D may just be used + const primitive2d::Primitive2DContainer& rContent = rTransparenceCandidate.getChildren(); + const primitive2d::Primitive2DContainer& rTransparence + = rTransparenceCandidate.getTransparence(); + + if (rContent.empty() || rTransparence.empty()) + return; + + // try to identify a single FillGradientPrimitive2D in the + // transparence part of the primitive + const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr; + static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore + + if (!bForceToBigTransparentVDev && 1 == rTransparence.size()) + { + const primitive2d::Primitive2DReference xReference(rTransparence[0]); + pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(xReference.get()); + } - void VclMetafileProcessor2D::processStructureTagPrimitive2D(const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate) + // Check also for correct ID to exclude derived implementations + if (pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID()) + { + // various content, create content-metafile + GDIMetaFile aContentMetafile; + const tools::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 no one constructs + // transparence primitives with non-trivial transparence content) i will for now not + // refine to tiling here. + + basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D())); + aViewRange.transform(maCurrentTransformation); + const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())), + static_cast<sal_Int32>(floor(aViewRange.getMinY())), + static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), + static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); + const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic)); + Size aSizePixel(aRectPixel.GetSize()); + const Point aEmptyPoint; + ScopedVclPtrInstance<VirtualDevice> aBufferDevice; + const sal_uInt32 nMaxQuadratPixels(500000); + const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight()); + double fReduceFactor(1.0); + + if (nViewVisibleArea > nMaxQuadratPixels) { - // structured tag primitive - const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement()); - bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement)); + // reduce render size + fReduceFactor = sqrt(double(nMaxQuadratPixels) / static_cast<double>(nViewVisibleArea)); + aSizePixel = Size( + basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * fReduceFactor), + basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * fReduceFactor)); + } - if(mpPDFExtOutDevData && bTagUsed) + 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 MapUnit::MapInch needs to be used. + basegfx::B2DHomMatrix aViewTransform(aBufferDevice->GetViewTransformation()); + const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); + const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); + const double fDPIXChange(static_cast<double>(aDPIOld.getWidth()) + / static_cast<double>(aDPINew.getWidth())); + const double fDPIYChange(static_cast<double>(aDPIOld.getHeight()) + / static_cast<double>(aDPINew.getHeight())); + + if (!basegfx::fTools::equal(fDPIXChange, 1.0) + || !basegfx::fTools::equal(fDPIYChange, 1.0)) { - // foreground object: tag as regular structure element - if (!rStructureTagCandidate.isBackground()) - { - mpPDFExtOutDevData->BeginStructureElement(rTagElement); - } - // background object - else - { - // background image: tag as artifact - if (rStructureTagCandidate.isImage()) - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement); - // any other background object: do not tag - else - bTagUsed = false; - } + aViewTransform.scale(fDPIXChange, fDPIYChange); } - // process children normally - process(rStructureTagCandidate.getChildren()); - - if(mpPDFExtOutDevData && bTagUsed) + // also take scaling from Size reduction into account + if (!basegfx::fTools::equal(fReduceFactor, 1.0)) { - // write end tag - mpPDFExtOutDevData->EndStructureElement(); + aViewTransform.scale(fReduceFactor, fReduceFactor); } + + // 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)); + + // paint + mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(), + BitmapEx(aBmContent, aBmAlpha)); + } + } +} + +void VclMetafileProcessor2D::processStructureTagPrimitive2D( + const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate) +{ + // structured tag primitive + const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement()); + bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement)); + + if (mpPDFExtOutDevData && bTagUsed) + { + // foreground object: tag as regular structure element + if (!rStructureTagCandidate.isBackground()) + { + mpPDFExtOutDevData->BeginStructureElement(rTagElement); } + // background object + else + { + // background image: tag as artifact + if (rStructureTagCandidate.isImage()) + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement); + // any other background object: do not tag + else + bTagUsed = false; + } + } + + // process children normally + process(rStructureTagCandidate.getChildren()); + + if (mpPDFExtOutDevData && bTagUsed) + { + // write end tag + mpPDFExtOutDevData->EndStructureElement(); + } +} } // end of namespace diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx index bf2cc266a685..0393039f4455 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx @@ -27,163 +27,182 @@ #include <vcl/pdfextoutdevdata.hxx> // vcl::PDFExtOutDevData support class GDIMetaFile; -namespace tools { class Rectangle; } +namespace tools +{ +class Rectangle; +} class Gradient; class SvtGraphicFill; class SvtGraphicStroke; -namespace drawinglayer::attribute { - class FillGradientAttribute; - class LineAttribute; - class StrokeAttribute; - class LineStartEndAttribute; +namespace drawinglayer::attribute +{ +class FillGradientAttribute; +class LineAttribute; +class StrokeAttribute; +class LineStartEndAttribute; } -namespace drawinglayer::primitive2d { - class GraphicPrimitive2D; - class ControlPrimitive2D; - class TextHierarchyFieldPrimitive2D; - class TextHierarchyLinePrimitive2D; - class TextHierarchyBulletPrimitive2D; - class TextHierarchyParagraphPrimitive2D; - class TextHierarchyBlockPrimitive2D; - class TextSimplePortionPrimitive2D; - class PolygonHairlinePrimitive2D; - class PolygonStrokePrimitive2D; - class PolygonStrokeArrowPrimitive2D; - class PolyPolygonGraphicPrimitive2D; - class PolyPolygonHatchPrimitive2D; - class PolyPolygonGradientPrimitive2D; - class PolyPolygonColorPrimitive2D; - class MaskPrimitive2D; - class UnifiedTransparencePrimitive2D; - class TransparencePrimitive2D; - class StructureTagPrimitive2D; +namespace drawinglayer::primitive2d +{ +class GraphicPrimitive2D; +class ControlPrimitive2D; +class TextHierarchyFieldPrimitive2D; +class TextHierarchyLinePrimitive2D; +class TextHierarchyBulletPrimitive2D; +class TextHierarchyParagraphPrimitive2D; +class TextHierarchyBlockPrimitive2D; +class TextSimplePortionPrimitive2D; +class PolygonHairlinePrimitive2D; +class PolygonStrokePrimitive2D; +class PolygonStrokeArrowPrimitive2D; +class PolyPolygonGraphicPrimitive2D; +class PolyPolygonHatchPrimitive2D; +class PolyPolygonGradientPrimitive2D; +class PolyPolygonColorPrimitive2D; +class MaskPrimitive2D; +class UnifiedTransparencePrimitive2D; +class TransparencePrimitive2D; +class StructureTagPrimitive2D; } -namespace basegfx { - class BColor; +namespace basegfx +{ +class BColor; } namespace drawinglayer::processor2d { - /** VclMetafileProcessor2D class - - This processor derived from VclProcessor2D is the base class for rendering - all fed primitives to a classical VCL-Metafile, including all over the - time grown extra data in comments and PDF exception data creations. Also - printing needs some exception stuff. - - All in all it is needed to emulate the old ::paint output from the old - Drawinglayer as long as exporters and/or filters still use the Metafile - and the extra-data added to it (which can be seen mostly as 'extensions' - or simply as 'hacks'). - */ - class VclMetafileProcessor2D : public VclProcessor2D - { - private: - /// local helper(s) - ::tools::Rectangle impDumpToMetaFile( - const primitive2d::Primitive2DContainer& rContent, - GDIMetaFile& o_rContentMetafile); - void impConvertFillGradientAttributeToVCLGradient( - Gradient& o_rVCLGradient, - const attribute::FillGradientAttribute& rFiGrAtt, - bool bIsTransparenceGradient) const; - void impStartSvtGraphicFill(SvtGraphicFill const * pSvtGraphicFill); - void impEndSvtGraphicFill(SvtGraphicFill const * pSvtGraphicFill); - std::unique_ptr<SvtGraphicStroke> 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); - void impStartSvtGraphicStroke(SvtGraphicStroke const * pSvtGraphicStroke); - void impEndSvtGraphicStroke(SvtGraphicStroke const * pSvtGraphicStroke); - void popStructureElement(vcl::PDFWriter::StructElement eElem); - void popListItem(); - void popList(); - - void processGraphicPrimitive2D(const primitive2d::GraphicPrimitive2D& rGraphicPrimitive); - void processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive); - void processTextHierarchyFieldPrimitive2D(const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive); - void processTextHierarchyLinePrimitive2D(const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive); - void processTextHierarchyBulletPrimitive2D(const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive); - void processTextHierarchyParagraphPrimitive2D(const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive); - void processTextHierarchyBlockPrimitive2D(const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive); - void processTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate); - void processPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive); - void processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive); - void processPolygonStrokeArrowPrimitive2D(const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive); - void processPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate); - void processPolyPolygonHatchPrimitive2D(const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate); - void processPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate); - void processPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate); - void processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate); - void processUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate); - void processTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransparenceCandidate); - void processStructureTagPrimitive2D(const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate); - - /// Convert the fWidth to the same space as its coordinates. - double getTransformedLineWidth( double fWidth ) const; - - /// the current clipping tools::PolyPolygon from MaskPrimitive2D - basegfx::B2DPolyPolygon maClipPolyPolygon; - - /// the target MetaFile - GDIMetaFile* mpMetaFile; - - /* do not allow embedding SvtGraphicFills into each other, - use a counter to prevent that - */ - sal_uInt32 mnSvtGraphicFillCount; - - /// same for SvtGraphicStroke - sal_uInt32 mnSvtGraphicStrokeCount; - - /* hold the last unified transparence value to have it handy - on SvtGraphicStroke creation - */ - double mfCurrentUnifiedTransparence; - - /* break iterator support - made static so it only needs to be fetched once, even with many single - constructed VclMetafileProcessor2D. It's still incarnated on demand, - but exists for OOo runtime now by purpose. - */ - static css::uno::Reference< css::i18n::XBreakIterator > mxBreakIterator; - - /* vcl::PDFExtOutDevData support - For the first step, some extra actions at vcl::PDFExtOutDevData need to - be emulated with the VclMetafileProcessor2D. These are potentially temporarily - since PDF export may use PrimitiveSequences one day directly. - */ - vcl::PDFExtOutDevData* mpPDFExtOutDevData; - - // Remember the current OutlineLevel. This is used when tagged PDF export - // is used to create/write valid structured list entries using PDF statements - // like '/L', '/LI', 'LBody' instead of simple '/P' (Paragraph). - // The value -1 means 'no OutlineLevel' and values >= 0 express the level. - sal_Int16 mnCurrentOutlineLevel; - bool mbInListItem; - bool mbBulletPresent; - - std::stack<vcl::PDFWriter::StructElement> maListElements; - - protected: - /* the local processor for BasePrimitive2D-Implementation based primitives, - called from the common process()-implementation - */ - virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) override; - - public: - /// constructor/destructor - VclMetafileProcessor2D( - const geometry::ViewInformation2D& rViewInformation, - OutputDevice& rOutDev); - virtual ~VclMetafileProcessor2D() override; - }; +/** VclMetafileProcessor2D class + + This processor derived from VclProcessor2D is the base class for rendering + all fed primitives to a classical VCL-Metafile, including all over the + time grown extra data in comments and PDF exception data creations. Also + printing needs some exception stuff. + + All in all it is needed to emulate the old ::paint output from the old + Drawinglayer as long as exporters and/or filters still use the Metafile + and the extra-data added to it (which can be seen mostly as 'extensions' + or simply as 'hacks'). + */ +class VclMetafileProcessor2D : public VclProcessor2D +{ +private: + tools::Rectangle impDumpToMetaFile(const primitive2d::Primitive2DContainer& rContent, + GDIMetaFile& o_rContentMetafile); + void + impConvertFillGradientAttributeToVCLGradient(Gradient& o_rVCLGradient, + const attribute::FillGradientAttribute& rFiGrAtt, + bool bIsTransparenceGradient) const; + void impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill); + void impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill); + std::unique_ptr<SvtGraphicStroke> + 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); + void impStartSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke); + void impEndSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke); + void popStructureElement(vcl::PDFWriter::StructElement eElem); + void popListItem(); + void popList(); + + void processGraphicPrimitive2D(const primitive2d::GraphicPrimitive2D& rGraphicPrimitive); + void processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive); + void processTextHierarchyFieldPrimitive2D( + const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive); + void processTextHierarchyLinePrimitive2D( + const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive); + void processTextHierarchyBulletPrimitive2D( + const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive); + void processTextHierarchyParagraphPrimitive2D( + const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive); + void processTextHierarchyBlockPrimitive2D( + const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive); + void processTextSimplePortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate); + void processPolygonHairlinePrimitive2D( + const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive); + void + processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive); + void processPolygonStrokeArrowPrimitive2D( + const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive); + void processPolyPolygonGraphicPrimitive2D( + const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate); + void processPolyPolygonHatchPrimitive2D( + const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate); + void processPolyPolygonGradientPrimitive2D( + const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate); + void processPolyPolygonColorPrimitive2D( + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate); + void processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate); + void processUnifiedTransparencePrimitive2D( + const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate); + void processTransparencePrimitive2D( + const primitive2d::TransparencePrimitive2D& rTransparenceCandidate); + void processStructureTagPrimitive2D( + const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate); + + /// Convert the fWidth to the same space as its coordinates. + double getTransformedLineWidth(double fWidth) const; + + /// the current clipping tools::PolyPolygon from MaskPrimitive2D + basegfx::B2DPolyPolygon maClipPolyPolygon; + + /// the target MetaFile + GDIMetaFile* mpMetaFile; + + /* do not allow embedding SvtGraphicFills into each other, + use a counter to prevent that + */ + sal_uInt32 mnSvtGraphicFillCount; + + /// same for SvtGraphicStroke + sal_uInt32 mnSvtGraphicStrokeCount; + + /* hold the last unified transparence value to have it handy + on SvtGraphicStroke creation + */ + double mfCurrentUnifiedTransparence; + + /* break iterator support + made static so it only needs to be fetched once, even with many single + constructed VclMetafileProcessor2D. It's still incarnated on demand, + but exists for OOo runtime now by purpose. + */ + static css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator; + + /* vcl::PDFExtOutDevData support + For the first step, some extra actions at vcl::PDFExtOutDevData need to + be emulated with the VclMetafileProcessor2D. These are potentially temporarily + since PDF export may use PrimitiveSequences one day directly. + */ + vcl::PDFExtOutDevData* mpPDFExtOutDevData; + + // Remember the current OutlineLevel. This is used when tagged PDF export + // is used to create/write valid structured list entries using PDF statements + // like '/L', '/LI', 'LBody' instead of simple '/P' (Paragraph). + // The value -1 means 'no OutlineLevel' and values >= 0 express the level. + sal_Int16 mnCurrentOutlineLevel; + bool mbInListItem; + bool mbBulletPresent; + + std::stack<vcl::PDFWriter::StructElement> maListElements; + +protected: + /* the local processor for BasePrimitive2D-Implementation based primitives, + called from the common process()-implementation + */ + virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) override; + +public: + /// constructor/destructor + VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation, + OutputDevice& rOutDev); + virtual ~VclMetafileProcessor2D() override; +}; } // end of namespace processor2d::drawinglayer /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist index c347c847cdc0..d11bb2aae9b4 100644 --- a/solenv/clang-format/blacklist +++ b/solenv/clang-format/blacklist @@ -3747,8 +3747,6 @@ drawinglayer/source/processor2d/objectinfoextractor2d.cxx drawinglayer/source/processor2d/processor2dtools.cxx drawinglayer/source/processor2d/processorfromoutputdevice.cxx drawinglayer/source/processor2d/textaspolygonextractor2d.cxx -drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx -drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx drawinglayer/source/processor3d/baseprocessor3d.cxx drawinglayer/source/processor3d/cutfindprocessor3d.cxx drawinglayer/source/processor3d/defaultprocessor3d.cxx |