diff options
author | Armin Le Grand <alg@apache.org> | 2012-01-17 16:54:04 +0000 |
---|---|---|
committer | Armin Le Grand <alg@apache.org> | 2012-01-17 16:54:04 +0000 |
commit | 2a0cd925bebb0c7d3513db311b185a04f259b68d (patch) | |
tree | 8d1ed154b241a9823c54b76ed091ecb6b6935a9d | |
parent | 63480c993d8e2fb0929d3c39f621aae9443faa04 (diff) |
linecap: Reintegrating finished LineCap feature, kudos to Regina Henschel for doing the basic implementation and offering it under apache license
Notes
Notes:
merged as: 44cfc7cb6533d827fd2d6e586d92c61d7d7f7a70
76 files changed, 1832 insertions, 309 deletions
diff --git a/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx b/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx index d90065f37c84..4e2270e75b6f 100644 --- a/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx +++ b/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx @@ -28,6 +28,7 @@ #include <basegfx/numeric/ftools.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> #include <basegfx/polygon/b2dpolygon.hxx> +#include <com/sun/star/drawing/LineCap.hpp> ////////////////////////////////////////////////////////////////////////////// @@ -101,6 +102,9 @@ namespace basegfx The LineJoin if the edges meeting in a point do not have a C1 or C2 continuity + @param eCap + The kind of cap, which is added to the line. + @param fMaxAllowedAngle Allows to hand over the maximum allowed angle between an edge and it's control vectors. The smaller, the more subdivisions will be @@ -128,6 +132,7 @@ namespace basegfx const B2DPolygon& rCandidate, double fHalfLineWidth, B2DLineJoin eJoin = B2DLINEJOIN_ROUND, + com::sun::star::drawing::LineCap eCap = com::sun::star::drawing::LineCap_BUTT, double fMaxAllowedAngle = (12.5 * F_PI180), double fMaxPartOfEdge = 0.4, double fMiterMinimumAngle = (15.0 * F_PI180)); diff --git a/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx b/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx index 11ccd395cf5d..592ab4b9209d 100644 --- a/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx +++ b/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx @@ -285,6 +285,9 @@ namespace basegfx */ B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius ); + /// create half circle centered on (0,0) from [0 .. F_PI] + B2DPolygon createHalfUnitCircle(); + /** create a polygon which describes the unit circle and close it @param nStartQuadrant diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx index e588be5368d2..b723cd5a63b1 100644 --- a/basegfx/source/polygon/b2dlinegeometry.cxx +++ b/basegfx/source/polygon/b2dlinegeometry.cxx @@ -34,6 +34,7 @@ #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/curve/b2dcubicbezier.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <com/sun/star/drawing/LineCap.hpp> ////////////////////////////////////////////////////////////////////////////// @@ -338,7 +339,13 @@ namespace basegfx } } - B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth) + B2DPolyPolygon createAreaGeometryForEdge( + const B2DCubicBezier& rEdge, + double fHalfLineWidth, + bool bStartRound, + bool bEndRound, + bool bStartSquare, + bool bEndSquare) { // create polygon for edge // Unfortunately, while it would be geometrically correct to not add @@ -347,35 +354,102 @@ namespace basegfx if(rEdge.isBezier()) { // prepare target and data common for upper and lower + B2DPolyPolygon aRetval; B2DPolygon aBezierPolygon; const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint()); const double fEdgeLength(aPureEdgeVector.getLength()); const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength)); - const B2DVector aTangentA(rEdge.getTangent(0.0)); - const B2DVector aTangentB(rEdge.getTangent(1.0)); + B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize(); + B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize(); + const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA)); + const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB)); + + // create upper displacement vectors and check if they cut + const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth); + const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth); + double fCutA(0.0); + const tools::CutFlagValue aCutA(tools::findCut( + rEdge.getStartPoint(), aPerpendStartA, + rEdge.getEndPoint(), aPerpendEndA, + CUTFLAG_ALL, &fCutA)); + const bool bCutA(CUTFLAG_NONE != aCutA); + + // create lower displacement vectors and check if they cut + const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth); + const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth); + double fCutB(0.0); + const tools::CutFlagValue aCutB(tools::findCut( + rEdge.getEndPoint(), aPerpendEndB, + rEdge.getStartPoint(), aPerpendStartB, + CUTFLAG_ALL, &fCutB)); + const bool bCutB(CUTFLAG_NONE != aCutB); + + // check if cut happens + const bool bCut(bCutA || bCutB); + + // create left edge + if(bStartRound || bStartSquare) + { + basegfx::B2DPolygon aStartPolygon; + + if(bStartRound) + { + aStartPolygon = tools::createHalfUnitCircle(); + aStartPolygon.transform( + tools::createScaleShearXRotateTranslateB2DHomMatrix( + fHalfLineWidth, fHalfLineWidth, + 0.0, + atan2(aTangentA.getY(), aTangentA.getX()) + F_PI2, + rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY())); + } + else // bStartSquare + { + const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth)); + + if(bCut) + { + aStartPolygon.append(rEdge.getStartPoint() + aPerpendStartB); + } + + aStartPolygon.append(aStart + aPerpendStartB); + aStartPolygon.append(aStart + aPerpendStartA); + + if(bCut) + { + aStartPolygon.append(rEdge.getStartPoint() + aPerpendStartA); + } + } + + if(bCut) + { + aStartPolygon.append(rEdge.getStartPoint()); + aStartPolygon.setClosed(true); + aRetval.append(aStartPolygon); + } + else + { + aBezierPolygon.append(aStartPolygon); + } + } + else + { + // append original in-between point + aBezierPolygon.append(rEdge.getStartPoint()); + } // create upper edge. { - // create displacement vectors and check if they cut - const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth); - const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth); - double fCut(0.0); - const tools::CutFlagValue aCut(tools::findCut( - rEdge.getStartPoint(), aPerpendStart, - rEdge.getEndPoint(), aPerpendEnd, - CUTFLAG_ALL, &fCut)); - - if(CUTFLAG_NONE != aCut) + if(bCutA) { // calculate cut point and add - const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut)); + const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStartA * fCutA)); aBezierPolygon.append(aCutPoint); } else { // create scaled bezier segment - const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart); - const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd); + const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA); + const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA); const B2DVector aEdge(aEnd - aStart); const double fLength(aEdge.getLength()); const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength); @@ -387,31 +461,69 @@ namespace basegfx } } - // append original in-between point - aBezierPolygon.append(rEdge.getEndPoint()); + // create right edge + if(bEndRound || bEndSquare) + { + basegfx::B2DPolygon aEndPolygon; + + if(bEndRound) + { + aEndPolygon = tools::createHalfUnitCircle(); + aEndPolygon.transform( + tools::createScaleShearXRotateTranslateB2DHomMatrix( + fHalfLineWidth, fHalfLineWidth, + 0.0, + atan2(aTangentB.getY(), aTangentB.getX()) - F_PI2, + rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY())); + } + else // bEndSquare + { + const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth)); + + if(bCut) + { + aEndPolygon.append(rEdge.getEndPoint() + aPerpendEndA); + } + + aEndPolygon.append(aEnd + aPerpendEndA); + aEndPolygon.append(aEnd + aPerpendEndB); + + if(bCut) + { + aEndPolygon.append(rEdge.getEndPoint() + aPerpendEndB); + } + } + + if(bCut) + { + aEndPolygon.append(rEdge.getEndPoint()); + aEndPolygon.setClosed(true); + aRetval.append(aEndPolygon); + } + else + { + aBezierPolygon.append(aEndPolygon); + } + } + else + { + // append original in-between point + aBezierPolygon.append(rEdge.getEndPoint()); + } // create lower edge. { - // create displacement vectors and check if they cut - const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth); - const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth); - double fCut(0.0); - const tools::CutFlagValue aCut(tools::findCut( - rEdge.getEndPoint(), aPerpendEnd, - rEdge.getStartPoint(), aPerpendStart, - CUTFLAG_ALL, &fCut)); - - if(CUTFLAG_NONE != aCut) + if(bCutB) { // calculate cut point and add - const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut)); + const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEndB * fCutB)); aBezierPolygon.append(aCutPoint); } else { // create scaled bezier segment - const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd); - const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart); + const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB); + const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB); const B2DVector aEdge(aEnd - aStart); const double fLength(aEdge.getLength()); const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength); @@ -423,39 +535,108 @@ namespace basegfx } } - // append original in-between point - aBezierPolygon.append(rEdge.getStartPoint()); - // close and return aBezierPolygon.setClosed(true); - return aBezierPolygon; + aRetval.append(aBezierPolygon); + + return aRetval; } else { - // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the - // full-length edge vector to have numerically exactly the same results as in the - // createAreaGeometryForJoin implementation - const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3); - const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth); + // Get start and end point, create tangent and set to needed length + B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint()); + aTangent.setLength(fHalfLineWidth); + + // prepare return value B2DPolygon aEdgePolygon; - // create upper edge - aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector); - aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector); + // buffered angle + double fAngle(0.0); + bool bAngle(false); - // append original in-between point - aEdgePolygon.append(rEdge.getEndPoint()); + // buffered perpendicular + B2DVector aPerpend; + bool bPerpend(false); - // create lower edge - aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector); - aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector); + // create left vertical + if(bStartRound) + { + aEdgePolygon = tools::createHalfUnitCircle(); + fAngle = atan2(aTangent.getY(), aTangent.getX()); + bAngle = true; + aEdgePolygon.transform( + tools::createScaleShearXRotateTranslateB2DHomMatrix( + fHalfLineWidth, fHalfLineWidth, + 0.0, + fAngle + F_PI2, + rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY())); + } + else + { + aPerpend.setX(-aTangent.getY()); + aPerpend.setY(aTangent.getX()); + bPerpend = true; - // append original in-between point - aEdgePolygon.append(rEdge.getStartPoint()); + if(bStartSquare) + { + const basegfx::B2DPoint aStart(rEdge.getStartPoint() - aTangent); + + aEdgePolygon.append(aStart + aPerpend); + aEdgePolygon.append(aStart - aPerpend); + } + else + { + aEdgePolygon.append(rEdge.getStartPoint() + aPerpend); + aEdgePolygon.append(rEdge.getStartPoint()); // keep the in-between point for numerical reasons + aEdgePolygon.append(rEdge.getStartPoint() - aPerpend); + } + } + + // create right vertical + if(bEndRound) + { + basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle()); + + if(!bAngle) + { + fAngle = atan2(aTangent.getY(), aTangent.getX()); + } + + aEndPolygon.transform( + tools::createScaleShearXRotateTranslateB2DHomMatrix( + fHalfLineWidth, fHalfLineWidth, + 0.0, + fAngle - F_PI2, + rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY())); + aEdgePolygon.append(aEndPolygon); + } + else + { + if(!bPerpend) + { + aPerpend.setX(-aTangent.getY()); + aPerpend.setY(aTangent.getX()); + } + + if(bEndSquare) + { + const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + aTangent); + + aEdgePolygon.append(aEnd - aPerpend); + aEdgePolygon.append(aEnd + aPerpend); + } + else + { + aEdgePolygon.append(rEdge.getEndPoint() - aPerpend); + aEdgePolygon.append(rEdge.getEndPoint()); // keep the in-between point for numerical reasons + aEdgePolygon.append(rEdge.getEndPoint() + aPerpend); + } + } // close and return aEdgePolygon.setClosed(true); - return aEdgePolygon; + + return B2DPolyPolygon(aEdgePolygon); } } @@ -574,6 +755,7 @@ namespace basegfx const B2DPolygon& rCandidate, double fHalfLineWidth, B2DLineJoin eJoin, + com::sun::star::drawing::LineCap eCap, double fMaxAllowedAngle, double fMaxPartOfEdge, double fMiterMinimumAngle) @@ -619,6 +801,7 @@ namespace basegfx const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin); const bool bIsClosed(aCandidate.isClosed()); const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1); + const bool bLineCap(!bIsClosed && com::sun::star::drawing::LineCap_BUTT != eCap); if(nEdgeCount) { @@ -649,16 +832,16 @@ namespace basegfx // check and create linejoin if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a)) { - const B2DVector aTangentPrev(aPrev.getTangent(1.0)); - const B2DVector aTangentEdge(aEdge.getTangent(0.0)); + B2DVector aTangentPrev(aPrev.getTangent(1.0)); aTangentPrev.normalize(); + B2DVector aTangentEdge(aEdge.getTangent(0.0)); aTangentEdge.normalize(); B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge)); if(ORIENTATION_NEUTRAL == aOrientation) { - // they are parallell or empty; if they are both not zero and point - // in opposite direction, a half-circle is needed - if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero()) - { + // they are parallell or empty; if they are both not zero and point + // in opposite direction, a half-circle is needed + if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero()) + { const double fAngle(fabs(aTangentPrev.angle(aTangentEdge))); if(fTools::equal(fAngle, F_PI)) @@ -672,38 +855,76 @@ namespace basegfx if(ORIENTATION_POSITIVE == aOrientation) { - const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth); - const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth); - - aRetval.append(createAreaGeometryForJoin( - aTangentPrev, aTangentEdge, - aPerpendPrev, aPerpendEdge, - aEdge.getStartPoint(), fHalfLineWidth, - eJoin, fMiterMinimumAngle)); + const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * -fHalfLineWidth); + const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * -fHalfLineWidth); + + aRetval.append( + createAreaGeometryForJoin( + aTangentPrev, + aTangentEdge, + aPerpendPrev, + aPerpendEdge, + aEdge.getStartPoint(), + fHalfLineWidth, + eJoin, + fMiterMinimumAngle)); } else if(ORIENTATION_NEGATIVE == aOrientation) { - const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth); - const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth); - - aRetval.append(createAreaGeometryForJoin( - aTangentEdge, aTangentPrev, - aPerpendEdge, aPerpendPrev, - aEdge.getStartPoint(), fHalfLineWidth, - eJoin, fMiterMinimumAngle)); + const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * fHalfLineWidth); + const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * fHalfLineWidth); + + aRetval.append( + createAreaGeometryForJoin( + aTangentEdge, + aTangentPrev, + aPerpendEdge, + aPerpendPrev, + aEdge.getStartPoint(), + fHalfLineWidth, + eJoin, + fMiterMinimumAngle)); } } // create geometry for edge - aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth)); + const bool bLast(a + 1 == nEdgeCount); - // prepare next step - if(bEventuallyCreateLineJoin) + if(bLineCap) + { + const bool bFirst(!a); + + aRetval.append( + createAreaGeometryForEdge( + aEdge, + fHalfLineWidth, + bFirst && com::sun::star::drawing::LineCap_ROUND == eCap, + bLast && com::sun::star::drawing::LineCap_ROUND == eCap, + bFirst && com::sun::star::drawing::LineCap_SQUARE == eCap, + bLast && com::sun::star::drawing::LineCap_SQUARE == eCap)); + } + else { - aPrev = aEdge; + aRetval.append( + createAreaGeometryForEdge( + aEdge, + fHalfLineWidth, + false, + false, + false, + false)); } - aEdge.setStartPoint(aEdge.getEndPoint()); + // prepare next step + if(!bLast) + { + if(bEventuallyCreateLineJoin) + { + aPrev = aEdge; + } + + aEdge.setStartPoint(aEdge.getEndPoint()); + } } } diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx index e95526898177..e8be1b3b10d5 100644 --- a/basegfx/source/polygon/b2dpolygontools.cxx +++ b/basegfx/source/polygon/b2dpolygontools.cxx @@ -1889,6 +1889,33 @@ namespace basegfx return aUnitCircle; } + B2DPolygon createHalfUnitCircle() + { + static B2DPolygon aUnitHalfCircle; + + if(!aUnitHalfCircle.count()) + { + const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER)); + const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER)); + B2DPoint aPoint(1.0, 0.0); + B2DPoint aForward(1.0, fScaledKappa); + B2DPoint aBackward(1.0, -fScaledKappa); + + aUnitHalfCircle.append(aPoint); + + for(sal_uInt32 a(0); a < STEPSPERQUARTER * 2; a++) + { + aPoint *= aRotateMatrix; + aBackward *= aRotateMatrix; + aUnitHalfCircle.appendBezierSegment(aForward, aBackward, aPoint); + aForward *= aRotateMatrix; + } + } + + return aUnitHalfCircle; + } + B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant) { switch(nStartQuadrant % 4) diff --git a/basegfx/source/polygon/b3dpolygontools.cxx b/basegfx/source/polygon/b3dpolygontools.cxx index 3f2ecd938bad..e573c0140eec 100644 --- a/basegfx/source/polygon/b3dpolygontools.cxx +++ b/basegfx/source/polygon/b3dpolygontools.cxx @@ -418,56 +418,59 @@ namespace basegfx const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength()); - while(fTools::less(fDotDashMovingLength, fEdgeLength)) + if(!fTools::equalZero(fEdgeLength)) { - // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] - const bool bHandleLine(bIsLine && pLineTarget); - const bool bHandleGap(!bIsLine && pGapTarget); - - if(bHandleLine || bHandleGap) + while(fTools::less(fDotDashMovingLength, fEdgeLength)) { - if(!aSnippet.count()) + // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] + const bool bHandleLine(bIsLine && pLineTarget); + const bool bHandleGap(!bIsLine && pGapTarget); + + if(bHandleLine || bHandleGap) { - aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); - } + if(!aSnippet.count()) + { + aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); + } - aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength)); + aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength)); - if(bHandleLine) - { - pLineTarget->append(aSnippet); - } - else - { - pGapTarget->append(aSnippet); + if(bHandleLine) + { + pLineTarget->append(aSnippet); + } + else + { + pGapTarget->append(aSnippet); + } + + aSnippet.clear(); } - aSnippet.clear(); + // prepare next DotDashArray step and flip line/gap flag + fLastDotDashMovingLength = fDotDashMovingLength; + fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; + bIsLine = !bIsLine; } - // prepare next DotDashArray step and flip line/gap flag - fLastDotDashMovingLength = fDotDashMovingLength; - fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount]; - bIsLine = !bIsLine; - } - - // append snippet [fLastDotDashMovingLength, fEdgeLength] - const bool bHandleLine(bIsLine && pLineTarget); - const bool bHandleGap(!bIsLine && pGapTarget); + // append snippet [fLastDotDashMovingLength, fEdgeLength] + const bool bHandleLine(bIsLine && pLineTarget); + const bool bHandleGap(!bIsLine && pGapTarget); - if(bHandleLine || bHandleGap) - { - if(!aSnippet.count()) + if(bHandleLine || bHandleGap) { - aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); + if(!aSnippet.count()) + { + aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); + } + + aSnippet.append(aNextPoint); } - aSnippet.append(aNextPoint); + // prepare move to next edge + fDotDashMovingLength -= fEdgeLength; } - // prepare move to next edge - fDotDashMovingLength -= fEdgeLength; - // prepare next edge step (end point gets new start point) aCurrentPoint = aNextPoint; } diff --git a/basegfx/source/polygon/b3dpolypolygontools.cxx b/basegfx/source/polygon/b3dpolypolygontools.cxx index 5895990712c4..cd1a96e3d0cd 100644 --- a/basegfx/source/polygon/b3dpolypolygontools.cxx +++ b/basegfx/source/polygon/b3dpolypolygontools.cxx @@ -34,6 +34,11 @@ #include <osl/mutex.hxx> ////////////////////////////////////////////////////////////////////////////// +// predefines +#define nMinSegments sal_uInt32(1) +#define nMaxSegments sal_uInt32(512) + +////////////////////////////////////////////////////////////////////////////// namespace basegfx { @@ -268,20 +273,16 @@ namespace basegfx nHorSeg = fround(fabs(fHorStop - fHorStart) / (F_2PI / 24.0)); } - if(!nHorSeg) - { - nHorSeg = 1L; - } + // min/max limitations + nHorSeg = ::std::min(nMaxSegments, ::std::max(nMinSegments, nHorSeg)); if(!nVerSeg) { nVerSeg = fround(fabs(fVerStop - fVerStart) / (F_2PI / 24.0)); } - if(!nVerSeg) - { - nVerSeg = 1L; - } + // min/max limitations + nVerSeg = ::std::min(nMaxSegments, ::std::max(nMinSegments, nVerSeg)); // create constants const double fVerDiffPerStep((fVerStop - fVerStart) / (double)nVerSeg); @@ -371,20 +372,16 @@ namespace basegfx nHorSeg = fround(fabs(fHorStop - fHorStart) / (F_2PI / 24.0)); } - if(!nHorSeg) - { - nHorSeg = 1L; - } + // min/max limitations + nHorSeg = ::std::min(nMaxSegments, ::std::max(nMinSegments, nHorSeg)); if(!nVerSeg) { nVerSeg = fround(fabs(fVerStop - fVerStart) / (F_2PI / 24.0)); } - if(!nVerSeg) - { - nVerSeg = 1L; - } + // min/max limitations + nVerSeg = ::std::min(nMaxSegments, ::std::max(nMinSegments, nVerSeg)); // vertical loop for(sal_uInt32 a(0L); a < nVerSeg; a++) diff --git a/canvas/source/vcl/canvashelper.cxx b/canvas/source/vcl/canvashelper.cxx index 41550361c202..0dcc74fd03fc 100644 --- a/canvas/source/vcl/canvashelper.cxx +++ b/canvas/source/vcl/canvashelper.cxx @@ -35,6 +35,7 @@ #include <com/sun/star/rendering/TexturingMode.hpp> #include <com/sun/star/rendering/PathCapType.hpp> #include <com/sun/star/rendering/PathJoinType.hpp> +#include <com/sun/star/drawing/LineCap.hpp> #include <tools/poly.hxx> #include <vcl/window.hxx> @@ -94,6 +95,26 @@ namespace vclcanvas return basegfx::B2DLINEJOIN_NONE; } + + drawing::LineCap unoCapeFromCap( sal_Int8 nCapType) + { + switch ( nCapType) + { + case rendering::PathCapType::BUTT: + return drawing::LineCap_BUTT; + + case rendering::PathCapType::ROUND: + return drawing::LineCap_ROUND; + + case rendering::PathCapType::SQUARE: + return drawing::LineCap_SQUARE; + + default: + ENSURE_OR_THROW( false, + "unoCapeFromCap(): Unexpected cap type" ); + } + return drawing::LineCap_BUTT; + } } CanvasHelper::CanvasHelper() : @@ -384,7 +405,10 @@ namespace vclcanvas // AW: New interface, will create bezier polygons now aStrokedPolyPoly.append(basegfx::tools::createAreaGeometry( - aPolyPoly.getB2DPolygon(i), strokeAttributes.StrokeWidth*0.5, b2DJoineFromJoin(strokeAttributes.JoinType))); + aPolyPoly.getB2DPolygon(i), + strokeAttributes.StrokeWidth*0.5, + b2DJoineFromJoin(strokeAttributes.JoinType), + unoCapeFromCap(strokeAttributes.StartCapType))); //aStrokedPolyPoly.append( // ::basegfx::tools::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i), // strokeAttributes.StrokeWidth*0.5, diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx index a395092f1ad3..d02bb0551b42 100644 --- a/cppcanvas/source/mtfrenderer/implrenderer.cxx +++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx @@ -289,6 +289,28 @@ namespace break; } + switch(rLineInfo.GetLineCap()) + { + default: /* com::sun::star::drawing::LineCap_BUTT */ + { + o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; + o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + o_rStrokeAttributes.StartCapType = rendering::PathCapType::ROUND; + o_rStrokeAttributes.EndCapType = rendering::PathCapType::ROUND; + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + o_rStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE; + o_rStrokeAttributes.EndCapType = rendering::PathCapType::SQUARE; + break; + } + } + if( LINE_DASH == rLineInfo.GetStyle() ) { const ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); diff --git a/cui/source/inc/cuitabline.hxx b/cui/source/inc/cuitabline.hxx index 08d29b1f3476..828dd7527196 100644 --- a/cui/source/inc/cuitabline.hxx +++ b/cui/source/inc/cuitabline.hxx @@ -127,6 +127,10 @@ private: FixedText maFTEdgeStyle; LineEndLB maLBEdgeStyle; + // LineCaps + FixedText maFTCapStyle; + LineEndLB maLBCapStyle; + //#58425# Symbole auf einer Linie (z.B. StarChart) -> SdrObjList* pSymbolList; //a list of symbols to be shown in menu. Symbol at position SID_ATTR_SYMBOLTYPE is to be shown in preview. The list position is to be used cyclic. bool bNewSize; @@ -197,6 +201,9 @@ private: // #116827# DECL_LINK( ChangeEdgeStyleHdl_Impl, void * ); + // LineCaps + DECL_LINK ( ChangeCapStyleHdl_Impl, void * ); + sal_Bool FillXLSet_Impl(); #endif diff --git a/cui/source/tabpages/tabline.hrc b/cui/source/tabpages/tabline.hrc index 30dd2cb1a125..8319873eece8 100644 --- a/cui/source/tabpages/tabline.hrc +++ b/cui/source/tabpages/tabline.hrc @@ -97,6 +97,12 @@ #define FT_EDGE_STYLE 13 #define LB_EDGE_STYLE 5 +// since LO3.6 +#define FT_CAP_STYLE 14 +#define LB_CAP_STYLE 6 +// not sure about IAccessibility2, add it nevertheless +#define STR_LB_CAP_STYLE 42 + //Symbole (fuer StarChart) #define FT_SYMBOL_WIDTH 20 #define FT_SYMBOL_HEIGHT 21 diff --git a/cui/source/tabpages/tabline.src b/cui/source/tabpages/tabline.src index eda4161bb138..4538b01c25ac 100644 --- a/cui/source/tabpages/tabline.src +++ b/cui/source/tabpages/tabline.src @@ -235,13 +235,13 @@ TabPage RID_SVXPAGE_LINE { Pos = MAP_APPFONT ( 124 , 3 + (16 * 6) - 4 ) ; Size = MAP_APPFONT ( 130 , 8 ) ; - Text [ en-US ] = "Corner style" ; + Text [ en-US ] = "Corner and cap styles" ; }; FixedText FT_EDGE_STYLE { Pos = MAP_APPFONT ( 130 , 14 + (16 * 6) - 4 ) ; Size = MAP_APPFONT ( 118 , 8 ) ; - Text [ en-US ] = "Sty~le" ; + Text [ en-US ] = "~Corner style" ; }; ListBox LB_EDGE_STYLE { @@ -259,6 +259,27 @@ TabPage RID_SVXPAGE_LINE < "Beveled" ; > ; }; }; + FixedText FT_CAP_STYLE + { + Pos = MAP_APPFONT ( 191 , 14 + (16 * 6) - 4 ) ; + Size = MAP_APPFONT ( 118 , 8 ) ; + Text [ en-US ] = "Ca~p style" ; + }; + ListBox LB_CAP_STYLE + { + HelpID = "cui:ListBox:RID_SVXPAGE_LINE:LB_CAP_STYLE"; + Border = TRUE ; + Pos = MAP_APPFONT ( 191 , 25 + (16 * 6) - 4 ) ; + Size = MAP_APPFONT ( 57 , 99 ) ; + TabStop = TRUE ; + DropDown = TRUE ; + StringList [ en-US ] = + { + < "Flat" ; Default ; > ; // Same string as in Excel + < "Round" ; > ; + < "Square" ; > ; + }; + }; ////////////////////////////////////////////////////////////////////////////// diff --git a/cui/source/tabpages/tpline.cxx b/cui/source/tabpages/tpline.cxx index 2627a6687659..2a4d59374815 100644 --- a/cui/source/tabpages/tpline.cxx +++ b/cui/source/tabpages/tpline.cxx @@ -129,6 +129,10 @@ SvxLineTabPage::SvxLineTabPage maFTEdgeStyle ( this, CUI_RES( FT_EDGE_STYLE ) ), maLBEdgeStyle ( this, CUI_RES( LB_EDGE_STYLE ) ), + // LineCaps + maFTCapStyle ( this, CUI_RES( FT_CAP_STYLE ) ), + maLBCapStyle ( this, CUI_RES( LB_CAP_STYLE ) ), + pSymbolList(NULL), bNewSize(false), nNumMenuGalleryItems(0), @@ -234,6 +238,10 @@ SvxLineTabPage::SvxLineTabPage Link aEdgeStyle = LINK( this, SvxLineTabPage, ChangeEdgeStyleHdl_Impl ); maLBEdgeStyle.SetSelectHdl( aEdgeStyle ); + // LineCaps + Link aCapStyle = LINK( this, SvxLineTabPage, ChangeCapStyleHdl_Impl ); + maLBCapStyle.SetSelectHdl( aCapStyle ); + //#58425# Symbole auf einer Linie (z.B. StarChart) , MB-Handler setzen aSymbolMB.SetSelectHdl(LINK(this, SvxLineTabPage, GraphicHdl_Impl)); aSymbolMB.SetActivateHdl(LINK(this, SvxLineTabPage, MenuCreateHdl_Impl)); @@ -484,6 +492,10 @@ void SvxLineTabPage::ActivatePage( const SfxItemSet& rSet ) maFLEdgeStyle.Hide(); maFTEdgeStyle.Hide(); maLBEdgeStyle.Hide(); + + // LineCaps + maFTCapStyle.Hide(); + maLBCapStyle.Hide(); } } @@ -751,6 +763,45 @@ sal_Bool SvxLineTabPage::FillItemSet( SfxItemSet& rAttrs ) } } + // LineCaps + nPos = maLBCapStyle.GetSelectEntryPos(); + if( LISTBOX_ENTRY_NOTFOUND != nPos && nPos != maLBCapStyle.GetSavedValue() ) + { + XLineCapItem* pNew = 0L; + + switch(nPos) + { + case 0: // Butt (=Flat), default + { + pNew = new XLineCapItem(com::sun::star::drawing::LineCap_BUTT); + break; + } + case 1: // Round + { + pNew = new XLineCapItem(com::sun::star::drawing::LineCap_ROUND); + break; + } + case 2: // Square + { + pNew = new XLineCapItem(com::sun::star::drawing::LineCap_SQUARE); + break; + } + } + + if(pNew) + { + pOld = GetOldItem( rAttrs, XATTR_LINECAP ); + + if(!pOld || !(*(const XLineCapItem*)pOld == *pNew)) + { + rAttrs.Put( *pNew ); + bModified = sal_True; + } + + delete pNew; + } + } + if(nSymbolType!=SVX_SYMBOLTYPE_UNKNOWN || bNewSize) { //wurde also per Auswahl gesetzt oder Gr��e ist anders @@ -866,6 +917,30 @@ sal_Bool SvxLineTabPage::FillXLSet_Impl() } } + // LineCaps + nPos = maLBCapStyle.GetSelectEntryPos(); + if(LISTBOX_ENTRY_NOTFOUND != nPos) + { + switch(nPos) + { + case 0: // Butt (=Flat), default + { + rXLSet.Put(XLineCapItem(com::sun::star::drawing::LineCap_BUTT)); + break; + } + case 1: // Round + { + rXLSet.Put(XLineCapItem(com::sun::star::drawing::LineCap_ROUND)); + break; + } + case 2: // Square + { + rXLSet.Put(XLineCapItem(com::sun::star::drawing::LineCap_SQUARE)); + break; + } + } + } + rXLSet.Put( XLineStartWidthItem( GetCoreValue( aMtrStartWidth, ePoolUnit ) ) ); rXLSet.Put( XLineEndWidthItem( GetCoreValue( aMtrEndWidth, ePoolUnit ) ) ); @@ -1284,6 +1359,28 @@ void SvxLineTabPage::Reset( const SfxItemSet& rAttrs ) } */ + // fdo#43209 + if(bObjSelected && SFX_ITEM_DEFAULT == rAttrs.GetItemState(XATTR_LINECAP)) + { + maFTCapStyle.Disable(); + maLBCapStyle.Disable(); + } + else if(SFX_ITEM_DONTCARE != rAttrs.GetItemState(XATTR_LINECAP)) + { + const com::sun::star::drawing::LineCap eLineCap(((const XLineCapItem&)(rAttrs.Get(XATTR_LINECAP))).GetValue()); + + switch(eLineCap) + { + case com::sun::star::drawing::LineCap_ROUND: maLBCapStyle.SelectEntryPos(1); break; + case com::sun::star::drawing::LineCap_SQUARE : maLBCapStyle.SelectEntryPos(2); break; + default /*com::sun::star::drawing::LineCap_BUTT*/: maLBCapStyle.SelectEntryPos(0); break; + } + } + else + { + maLBCapStyle.SetNoSelection(); + } + // Werte sichern aLbLineStyle.SaveValue(); aMtrLineWidth.SaveValue(); @@ -1299,6 +1396,9 @@ void SvxLineTabPage::Reset( const SfxItemSet& rAttrs ) // #116827# maLBEdgeStyle.SaveValue(); + // LineCaps + maLBCapStyle.SaveValue(); + ClickInvisibleHdl_Impl( this ); //ClickMeasuringHdl_Impl( this ); //aCtlPosition.Reset(); @@ -1416,6 +1516,15 @@ IMPL_LINK( SvxLineTabPage, ChangeEdgeStyleHdl_Impl, void *, EMPTYARG ) } //------------------------------------------------------------------------ +// fdo#43209 + +IMPL_LINK( SvxLineTabPage, ChangeCapStyleHdl_Impl, void *, EMPTYARG ) +{ + ChangePreviewHdl_Impl( this ); + + return( 0L ); +} +//------------------------------------------------------------------------ IMPL_LINK( SvxLineTabPage, ClickInvisibleHdl_Impl, void *, EMPTYARG ) { @@ -1442,6 +1551,10 @@ IMPL_LINK( SvxLineTabPage, ClickInvisibleHdl_Impl, void *, EMPTYARG ) // #116827# maFTEdgeStyle.Disable(); maLBEdgeStyle.Disable(); + + // LineCaps + maFTCapStyle.Disable(); + maLBCapStyle.Disable(); } } else @@ -1466,6 +1579,10 @@ IMPL_LINK( SvxLineTabPage, ClickInvisibleHdl_Impl, void *, EMPTYARG ) // #116827# maFTEdgeStyle.Enable(); maLBEdgeStyle.Enable(); + + // LineCaps + maFTCapStyle.Enable(); + maLBCapStyle.Enable(); } } ChangePreviewHdl_Impl( NULL ); diff --git a/drawinglayer/inc/drawinglayer/attribute/lineattribute.hxx b/drawinglayer/inc/drawinglayer/attribute/lineattribute.hxx index 15521ebeccd8..f46689f78f1d 100644 --- a/drawinglayer/inc/drawinglayer/attribute/lineattribute.hxx +++ b/drawinglayer/inc/drawinglayer/attribute/lineattribute.hxx @@ -26,6 +26,7 @@ #include <drawinglayer/drawinglayerdllapi.h> #include <basegfx/vector/b2enums.hxx> +#include <com/sun/star/drawing/LineCap.hpp> ////////////////////////////////////////////////////////////////////////////// // predefines @@ -54,7 +55,8 @@ namespace drawinglayer LineAttribute( const basegfx::BColor& rColor, double fWidth = 0.0, - basegfx::B2DLineJoin aB2DLineJoin = basegfx::B2DLINEJOIN_ROUND); + basegfx::B2DLineJoin aB2DLineJoin = basegfx::B2DLINEJOIN_ROUND, + com::sun::star::drawing::LineCap aLineCap = com::sun::star::drawing::LineCap_BUTT); LineAttribute(); LineAttribute(const LineAttribute& rCandidate); LineAttribute& operator=(const LineAttribute& rCandidate); @@ -70,6 +72,7 @@ namespace drawinglayer const basegfx::BColor& getColor() const; double getWidth() const; basegfx::B2DLineJoin getLineJoin() const; + com::sun::star::drawing::LineCap getLineCap() const; }; } // end of namespace attribute } // end of namespace drawinglayer diff --git a/drawinglayer/inc/drawinglayer/attribute/sdrlineattribute.hxx b/drawinglayer/inc/drawinglayer/attribute/sdrlineattribute.hxx index 78dbbdcff6ee..1af59daf42ba 100644 --- a/drawinglayer/inc/drawinglayer/attribute/sdrlineattribute.hxx +++ b/drawinglayer/inc/drawinglayer/attribute/sdrlineattribute.hxx @@ -38,6 +38,7 @@ #include <drawinglayer/drawinglayerdllapi.h> #include <basegfx/vector/b2enums.hxx> +#include <com/sun/star/drawing/LineCap.hpp> #include <vector> ////////////////////////////////////////////////////////////////////////////// @@ -69,6 +70,7 @@ namespace drawinglayer double fWidth, double fTransparence, const basegfx::BColor& rColor, + com::sun::star::drawing::LineCap eCap, const ::std::vector< double >& rDotDashArray, double fFullDotDashLen); SdrLineAttribute(const basegfx::BColor& rColor); @@ -90,6 +92,7 @@ namespace drawinglayer const basegfx::BColor& getColor() const; const ::std::vector< double >& getDotDashArray() const; double getFullDotDashLen() const; + com::sun::star::drawing::LineCap getCap() const; // bool access bool isDashed() const; diff --git a/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx b/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx index 438b3bcb4f36..b00b3b369722 100644 --- a/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx +++ b/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx @@ -59,6 +59,7 @@ namespace drawinglayer double mfDegreeStepWidth; double mfMiterMinimumAngle; basegfx::B2DLineJoin maLineJoin; + com::sun::star::drawing::LineCap maLineCap; protected: /** access methods to maLast3DDecomposition. The usage of this methods may allow @@ -76,7 +77,9 @@ namespace drawinglayer PolygonTubePrimitive3D( const basegfx::B3DPolygon& rPolygon, const basegfx::BColor& rBColor, - double fRadius, basegfx::B2DLineJoin aLineJoin, + double fRadius, + basegfx::B2DLineJoin aLineJoin, + com::sun::star::drawing::LineCap aLineCap, double fDegreeStepWidth = 10.0 * F_PI180, double fMiterMinimumAngle = 15.0 * F_PI180); @@ -85,6 +88,7 @@ namespace drawinglayer double getDegreeStepWidth() const { return mfDegreeStepWidth; } double getMiterMinimumAngle() const { return mfMiterMinimumAngle; } basegfx::B2DLineJoin getLineJoin() const { return maLineJoin; } + com::sun::star::drawing::LineCap getLineCap() const { return maLineCap; } /// compare operator virtual bool operator==(const BasePrimitive3D& rPrimitive) const; diff --git a/drawinglayer/source/attribute/lineattribute.cxx b/drawinglayer/source/attribute/lineattribute.cxx index 86576002b32f..da46682d2860 100644 --- a/drawinglayer/source/attribute/lineattribute.cxx +++ b/drawinglayer/source/attribute/lineattribute.cxx @@ -43,15 +43,18 @@ namespace drawinglayer basegfx::BColor maColor; // color double mfWidth; // absolute line width basegfx::B2DLineJoin meLineJoin; // type of LineJoin + com::sun::star::drawing::LineCap meLineCap; // BUTT, ROUND, or SQUARE ImpLineAttribute( const basegfx::BColor& rColor, double fWidth, - basegfx::B2DLineJoin aB2DLineJoin) + basegfx::B2DLineJoin aB2DLineJoin, + com::sun::star::drawing::LineCap aLineCap) : mnRefCount(0), maColor(rColor), mfWidth(fWidth), - meLineJoin(aB2DLineJoin) + meLineJoin(aB2DLineJoin), + meLineCap(aLineCap) { } @@ -59,12 +62,14 @@ namespace drawinglayer const basegfx::BColor& getColor() const { return maColor; } double getWidth() const { return mfWidth; } basegfx::B2DLineJoin getLineJoin() const { return meLineJoin; } + com::sun::star::drawing::LineCap getLineCap() const { return meLineCap; } bool operator==(const ImpLineAttribute& rCandidate) const { return (getColor() == rCandidate.getColor() && getWidth() == rCandidate.getWidth() - && getLineJoin() == rCandidate.getLineJoin()); + && getLineJoin() == rCandidate.getLineJoin() + && getLineCap() == rCandidate.getLineCap()); } static ImpLineAttribute* get_global_default() @@ -76,7 +81,8 @@ namespace drawinglayer pDefault = new ImpLineAttribute( basegfx::BColor(), 0.0, - basegfx::B2DLINEJOIN_ROUND); + basegfx::B2DLINEJOIN_ROUND, + com::sun::star::drawing::LineCap_BUTT); // never delete; start with RefCount 1, not 0 pDefault->mnRefCount++; @@ -89,9 +95,14 @@ namespace drawinglayer LineAttribute::LineAttribute( const basegfx::BColor& rColor, double fWidth, - basegfx::B2DLineJoin aB2DLineJoin) - : mpLineAttribute(new ImpLineAttribute( - rColor, fWidth, aB2DLineJoin)) + basegfx::B2DLineJoin aB2DLineJoin, + com::sun::star::drawing::LineCap aLineCap) + : mpLineAttribute( + new ImpLineAttribute( + rColor, + fWidth, + aB2DLineJoin, + aLineCap)) { } @@ -174,6 +185,11 @@ namespace drawinglayer return mpLineAttribute->getLineJoin(); } + com::sun::star::drawing::LineCap LineAttribute::getLineCap() const + { + return mpLineAttribute->getLineCap(); + } + } // end of namespace attribute } // end of namespace drawinglayer diff --git a/drawinglayer/source/attribute/sdrlineattribute.cxx b/drawinglayer/source/attribute/sdrlineattribute.cxx index 1850d919069f..5707bcbef0ec 100644 --- a/drawinglayer/source/attribute/sdrlineattribute.cxx +++ b/drawinglayer/source/attribute/sdrlineattribute.cxx @@ -56,6 +56,7 @@ namespace drawinglayer double mfWidth; // 1/100th mm, 0.0==hair double mfTransparence; // [0.0 .. 1.0], 0.0==no transp. basegfx::BColor maColor; // color of line + com::sun::star::drawing::LineCap meCap; // BUTT, ROUND, or SQUARE ::std::vector< double > maDotDashArray; // array of double which defines the dot-dash pattern double mfFullDotDashLen; // sum of maDotDashArray (for convenience) @@ -64,6 +65,7 @@ namespace drawinglayer double fWidth, double fTransparence, const basegfx::BColor& rColor, + com::sun::star::drawing::LineCap eCap, const ::std::vector< double >& rDotDashArray, double fFullDotDashLen) : mnRefCount(0), @@ -71,6 +73,7 @@ namespace drawinglayer mfWidth(fWidth), mfTransparence(fTransparence), maColor(rColor), + meCap(eCap), maDotDashArray(rDotDashArray), mfFullDotDashLen(fFullDotDashLen) { @@ -82,6 +85,7 @@ namespace drawinglayer mfWidth(0.0), mfTransparence(0.0), maColor(rColor), + meCap(com::sun::star::drawing::LineCap_BUTT), maDotDashArray(), mfFullDotDashLen(0.0) { @@ -92,6 +96,7 @@ namespace drawinglayer double getWidth() const { return mfWidth; } double getTransparence() const { return mfTransparence; } const basegfx::BColor& getColor() const { return maColor; } + com::sun::star::drawing::LineCap getCap() const { return meCap; } const ::std::vector< double >& getDotDashArray() const { return maDotDashArray; } double getFullDotDashLen() const { return mfFullDotDashLen; } @@ -101,6 +106,7 @@ namespace drawinglayer && getWidth() == rCandidate.getWidth() && getTransparence() == rCandidate.getTransparence() && getColor() == rCandidate.getColor() + && getCap() == rCandidate.getCap() && getDotDashArray() == rCandidate.getDotDashArray()); } @@ -115,6 +121,7 @@ namespace drawinglayer 0.0, 0.0, basegfx::BColor(), + com::sun::star::drawing::LineCap_BUTT, std::vector< double >(), 0.0); @@ -131,16 +138,26 @@ namespace drawinglayer double fWidth, double fTransparence, const basegfx::BColor& rColor, + com::sun::star::drawing::LineCap eCap, const ::std::vector< double >& rDotDashArray, double fFullDotDashLen) - : mpSdrLineAttribute(new ImpSdrLineAttribute( - eJoin, fWidth, fTransparence, rColor, rDotDashArray, fFullDotDashLen)) + : mpSdrLineAttribute( + new ImpSdrLineAttribute( + eJoin, + fWidth, + fTransparence, + rColor, + eCap, + rDotDashArray, + fFullDotDashLen)) { } SdrLineAttribute::SdrLineAttribute( const basegfx::BColor& rColor) - : mpSdrLineAttribute(new ImpSdrLineAttribute(rColor)) + : mpSdrLineAttribute( + new ImpSdrLineAttribute( + rColor)) { } @@ -243,6 +260,11 @@ namespace drawinglayer return (0L != getDotDashArray().size()); } + com::sun::star::drawing::LineCap SdrLineAttribute::getCap() const + { + return mpSdrLineAttribute->getCap(); + } + } // end of namespace attribute } // end of namespace drawinglayer diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx index a3eff771d4da..d8574093b23d 100644 --- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx @@ -706,7 +706,8 @@ namespace const drawinglayer::attribute::LineAttribute aLineAttribute( rProperties.getLineColor(), bWidthUsed ? rLineInfo.GetWidth() : 0.0, - rLineInfo.GetLineJoin()); + rLineInfo.GetLineJoin(), + rLineInfo.GetLineCap()); if(bDashDotUsed) { diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx index 1acf268f777c..1c8e150c6642 100644 --- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx @@ -32,6 +32,7 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <basegfx/polygon/b2dlinegeometry.hxx> +#include <com/sun/star/drawing/LineCap.hpp> ////////////////////////////////////////////////////////////////////////////// @@ -252,13 +253,17 @@ namespace drawinglayer // create fat line data const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0); const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin()); + const com::sun::star::drawing::LineCap aLineCap(getLineAttribute().getLineCap()); basegfx::B2DPolyPolygon aAreaPolyPolygon; for(sal_uInt32 a(0L); a < nCount; a++) { // New version of createAreaGeometry; now creates bezier polygons aAreaPolyPolygon.append(basegfx::tools::createAreaGeometry( - aHairLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, aLineJoin)); + aHairLinePolyPolygon.getB2DPolygon(a), + fHalfLineWidth, + aLineJoin, + aLineCap)); } // prepare return value @@ -339,10 +344,28 @@ namespace drawinglayer if(getLineAttribute().getWidth()) { + bool bUseDecomposition(false); + if(basegfx::B2DLINEJOIN_MITER == getLineAttribute().getLineJoin()) { // if line is mitered, use parent call since mitered line // geometry may use more space than the geometry grown by half line width + bUseDecomposition = true; + } + + if(!bUseDecomposition && com::sun::star::drawing::LineCap_SQUARE == getLineAttribute().getLineCap()) + { + // when drawing::LineCap_SQUARE is used the below method to grow the polygon + // range by half line width will not work, so use decomposition. Interestingly, + // the grow method below works perfectly for LineCap_ROUND since the grow is in + // all directions and the rounded cap needs the same grow in all directions independent + // from it's orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE + bUseDecomposition = true; + } + + if(bUseDecomposition) + { + // get correct range by using the decomposition fallback, reasons see above cases aRetval = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); } else diff --git a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx index 69dc188031ae..669dc5ee171e 100644 --- a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx @@ -106,11 +106,18 @@ namespace drawinglayer // create fat line data const double fRadius(getLineAttribute().getWidth() / 2.0); const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin()); + const com::sun::star::drawing::LineCap aLineCap(getLineAttribute().getLineCap()); for(sal_uInt32 a(0L); a < aHairLinePolyPolygon.count(); a++) { // create tube primitives - const Primitive3DReference xRef(new PolygonTubePrimitive3D(aHairLinePolyPolygon.getB3DPolygon(a), getLineAttribute().getColor(), fRadius, aLineJoin)); + const Primitive3DReference xRef( + new PolygonTubePrimitive3D( + aHairLinePolyPolygon.getB3DPolygon(a), + getLineAttribute().getColor(), + fRadius, + aLineJoin, + aLineCap)); aRetval[a] = xRef; } } diff --git a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx index 320e50872a3e..39c8952980bd 100644 --- a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx @@ -156,6 +156,76 @@ namespace drawinglayer return aLineCapList; } + Primitive3DSequence getLineCapRoundSegments( + sal_uInt32 nSegments, + const attribute::MaterialAttribute3D& rMaterial) + { + // static data for buffered tube primitives + static Primitive3DSequence aLineCapRoundList; + static sal_uInt32 nLineCapRoundSegments(0); + static attribute::MaterialAttribute3D aLineMaterial; + + // may exclusively change static data, use mutex + ::osl::Mutex m_mutex; + + if(nSegments != nLineCapRoundSegments || !(rMaterial == aLineMaterial)) + { + nLineCapRoundSegments = nSegments; + aLineMaterial = rMaterial; + aLineCapRoundList = Primitive3DSequence(); + } + + if(!aLineCapRoundList.hasElements() && nLineCapRoundSegments) + { + // calculate new horizontal segments + sal_uInt32 nVerSeg(nSegments / 2); + + if(nVerSeg < 1) + { + nVerSeg = 1; + } + + // create half-sphere; upper half of unit sphere + basegfx::B3DPolyPolygon aSphere( + basegfx::tools::createUnitSphereFillPolyPolygon( + nSegments, + nVerSeg, + true, + F_PI2, 0.0, + 0.0, F_2PI)); + const sal_uInt32 nCount(aSphere.count()); + + if(nCount) + { + // rotate to have sphere cap orientned to negative X-Axis; do not + // forget to transform normals, too + basegfx::B3DHomMatrix aSphereTrans; + + aSphereTrans.rotate(0.0, 0.0, F_PI2); + aSphere.transform(aSphereTrans); + aSphere.transformNormals(aSphereTrans); + + // realloc for primitives and create based on polygon snippets + aLineCapRoundList.realloc(nCount); + + for(sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B3DPolygon aPartPolygon(aSphere.getB3DPolygon(a)); + const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); + + // need to create one primitive per Polygon since the primitive + // is for planar PolyPolygons which is definitely not the case here + aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D( + aPartPolyPolygon, + rMaterial, + false); + } + } + } + + return aLineCapRoundList; + } + Primitive3DSequence getLineJoinSegments( sal_uInt32 nSegments, const attribute::MaterialAttribute3D& rMaterial, @@ -173,7 +243,7 @@ namespace drawinglayer if(basegfx::B2DLINEJOIN_ROUND == aLineJoin) { // calculate new horizontal segments - const sal_uInt32 nHorSeg((sal_uInt32)((fAngle / F_2PI) * (double)nSegments)); + const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments)); if(nHorSeg) { @@ -410,40 +480,88 @@ namespace drawinglayer const sal_uInt32 nPointCount(getB3DPolygon().count()); std::vector< BasePrimitive3D* > aResultVector; - if(0L != nPointCount) + if(nPointCount) { if(basegfx::fTools::more(getRadius(), 0.0)) { const attribute::MaterialAttribute3D aMaterial(getBColor()); - static sal_uInt32 nSegments(8L); // default for 3d line segments, for more quality just raise this value (in even steps) + static sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps) const bool bClosed(getB3DPolygon().isClosed()); const bool bNoLineJoin(basegfx::B2DLINEJOIN_NONE == getLineJoin()); - const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1L); - basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1L)); - basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0L)); + const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1); + basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1)); + basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0)); - for(sal_uInt32 a(0L); a < nLoopCount; a++) + for(sal_uInt32 a(0); a < nLoopCount; a++) { // get next data - const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1L) % nPointCount)); + const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount)); const basegfx::B3DVector aForw(aNext - aCurr); const double fForwLen(aForw.getLength()); if(basegfx::fTools::more(fForwLen, 0.0)) { + // find out if linecap is active + const bool bFirst(!a); + const bool bLast(a + 1 == nLoopCount); + const bool bLineCapPossible(!bClosed && (bFirst || bLast)); + const bool bLineCapRound(bLineCapPossible && com::sun::star::drawing::LineCap_ROUND == getLineCap()); + const bool bLineCapSquare(bLineCapPossible && com::sun::star::drawing::LineCap_SQUARE == getLineCap()); + // get rotation from vector, this describes rotation from (1, 0, 0) to aForw basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw)); - // create default transformation with scale and rotate - basegfx::B3DHomMatrix aVectorTrans; - aVectorTrans.scale(fForwLen, getRadius(), getRadius()); - aVectorTrans *= aRotVector; - aVectorTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); + // prepare transformations for tube and cap + basegfx::B3DHomMatrix aTubeTrans; + basegfx::B3DHomMatrix aCapTrans; + + // cap gets radius size + aCapTrans.scale(getRadius(), getRadius(), getRadius()); - if(bNoLineJoin || (!bClosed && !a)) + if(bLineCapSquare) + { + // when square line cap just prolong line segment in X, maybe 2 x radius when + // first and last (simple line segment) + const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius()); + + aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius()); + + if(bFirst) + { + // correct start positions for tube and cap when first and square prolonged + aTubeTrans.translate(-getRadius(), 0.0, 0.0); + aCapTrans.translate(-getRadius(), 0.0, 0.0); + } + } + else + { + // normal tube size + aTubeTrans.scale(fForwLen, getRadius(), getRadius()); + } + + // rotate and translate tube and cap + aTubeTrans *= aRotVector; + aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); + aCapTrans *= aRotVector; + aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); + + if(bNoLineJoin || (!bClosed && bFirst)) { // line start edge, build transformed primitiveVector3D - TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aVectorTrans, getLineCapSegments(nSegments, aMaterial)); + Primitive3DSequence aSequence; + + if(bLineCapRound && bFirst) + { + // LineCapRound used + aSequence = getLineCapRoundSegments(nSegments, aMaterial); + } + else + { + // simple closing cap + aSequence = getLineCapSegments(nSegments, aMaterial); + } + + TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence); aResultVector.push_back(pNewTransformedA); } else @@ -455,7 +573,14 @@ namespace drawinglayer { // line connect non-parallel, aBack, aForw, use getLineJoin() const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2 - Primitive3DSequence aNewList(getLineJoinSegments(nSegments, aMaterial, fAngle, getDegreeStepWidth(), getMiterMinimumAngle(), getLineJoin())); + Primitive3DSequence aNewList( + getLineJoinSegments( + nSegments, + aMaterial, + fAngle, + getDegreeStepWidth(), + getMiterMinimumAngle(), + getLineJoin())); // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack basegfx::B3DHomMatrix aInvRotVector(aRotVector); @@ -473,28 +598,61 @@ namespace drawinglayer aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); // line start edge, build transformed primitiveVector3D - TransformPrimitive3D* pNewTransformedB = new TransformPrimitive3D(aSphereTrans, aNewList); - aResultVector.push_back(pNewTransformedB); + aResultVector.push_back( + new TransformPrimitive3D( + aSphereTrans, + aNewList)); } } // create line segments, build transformed primitiveVector3D - TransformPrimitive3D* pNewTransformedC = new TransformPrimitive3D(aVectorTrans, getLineTubeSegments(nSegments, aMaterial)); - aResultVector.push_back(pNewTransformedC); + aResultVector.push_back( + new TransformPrimitive3D( + aTubeTrans, + getLineTubeSegments(nSegments, aMaterial))); - if(bNoLineJoin || (!bClosed && ((a + 1L) == nLoopCount))) + if(bNoLineJoin || (!bClosed && bLast)) { - // line end edge, first rotate (mirror) and translate, then use use aRotVector - basegfx::B3DHomMatrix aBackTrans; - aBackTrans.rotate(0.0, F_PI, 0.0); - aBackTrans.translate(1.0, 0.0, 0.0); - aBackTrans.scale(fForwLen, getRadius(), getRadius()); - aBackTrans *= aRotVector; - aBackTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); - - // line end edge, build transformed primitiveVector3D - TransformPrimitive3D* pNewTransformedD = new TransformPrimitive3D(aBackTrans, getLineCapSegments(nSegments, aMaterial)); - aResultVector.push_back(pNewTransformedD); + // line end edge + basegfx::B3DHomMatrix aBackCapTrans; + + // Mirror (line end) and radius scale + aBackCapTrans.rotate(0.0, F_PI, 0.0); + aBackCapTrans.scale(getRadius(), getRadius(), getRadius()); + + if(bLineCapSquare && bLast) + { + // correct position when square and prolonged + aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0); + } + else + { + // standard position + aBackCapTrans.translate(fForwLen, 0.0, 0.0); + } + + // rotate and translate to destination + aBackCapTrans *= aRotVector; + aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); + + // get primitiveVector3D + Primitive3DSequence aSequence; + + if(bLineCapRound && bLast) + { + // LineCapRound used + aSequence = getLineCapRoundSegments(nSegments, aMaterial); + } + else + { + // simple closing cap + aSequence = getLineCapSegments(nSegments, aMaterial); + } + + aResultVector.push_back( + new TransformPrimitive3D( + aBackCapTrans, + aSequence)); } } @@ -526,6 +684,7 @@ namespace drawinglayer const basegfx::B3DPolygon& rPolygon, const basegfx::BColor& rBColor, double fRadius, basegfx::B2DLineJoin aLineJoin, + com::sun::star::drawing::LineCap aLineCap, double fDegreeStepWidth, double fMiterMinimumAngle) : PolygonHairlinePrimitive3D(rPolygon, rBColor), @@ -533,7 +692,8 @@ namespace drawinglayer mfRadius(fRadius), mfDegreeStepWidth(fDegreeStepWidth), mfMiterMinimumAngle(fMiterMinimumAngle), - maLineJoin(aLineJoin) + maLineJoin(aLineJoin), + maLineCap(aLineCap) { } @@ -546,7 +706,8 @@ namespace drawinglayer return (getRadius() == rCompare.getRadius() && getDegreeStepWidth() == rCompare.getDegreeStepWidth() && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle() - && getLineJoin() == rCompare.getLineJoin()); + && getLineJoin() == rCompare.getLineJoin() + && getLineCap() == rCompare.getLineCap()); } return false; diff --git a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx index 87b7904e9885..08c79e7c8226 100644 --- a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx +++ b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx @@ -150,7 +150,7 @@ namespace drawinglayer aScaledPolyPolygon.transform(rObjectTransform); // create line and stroke attribute - const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin()); + const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap()); const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen()); // create primitives diff --git a/drawinglayer/source/processor2d/canvasprocessor.cxx b/drawinglayer/source/processor2d/canvasprocessor.cxx index 4bf7c0bd9dda..875f28003699 100644 --- a/drawinglayer/source/processor2d/canvasprocessor.cxx +++ b/drawinglayer/source/processor2d/canvasprocessor.cxx @@ -57,6 +57,7 @@ #include <com/sun/star/rendering/CompositeOperation.hpp> #include <com/sun/star/rendering/StrokeAttributes.hpp> #include <com/sun/star/rendering/PathJoinType.hpp> +#include <com/sun/star/rendering/PathCapType.hpp> #include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx> #include <com/sun/star/rendering/TexturingMode.hpp> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> @@ -1753,6 +1754,22 @@ namespace drawinglayer break; } + switch(rLineAttribute.getLineCap()) + { + case com::sun::star::drawing::LineCap_ROUND: + aStrokeAttribute.StartCapType = rendering::PathCapType::ROUND; + aStrokeAttribute.EndCapType = rendering::PathCapType::ROUND; + break; + case com::sun::star::drawing::LineCap_SQUARE: + aStrokeAttribute.StartCapType = rendering::PathCapType::SQUARE; + aStrokeAttribute.EndCapType = rendering::PathCapType::SQUARE; + break; + default: // com::sun::star::drawing::LineCap_BUTT + aStrokeAttribute.StartCapType = rendering::PathCapType::BUTT; + aStrokeAttribute.EndCapType = rendering::PathCapType::BUTT; + break; + } + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); maRenderState.DeviceColor = aHairlineColor.colorToDoubleSequence(mxCanvas->getDevice()); canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index 54cb95dc1672..21bc128e3edb 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -416,6 +416,7 @@ namespace drawinglayer } SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone); + SvtGraphicStroke::CapType eCap(SvtGraphicStroke::capButt); double fLineWidth(0.0); double fMiterLength(0.0); SvtGraphicStroke::DashArray aDashArray; @@ -455,6 +456,26 @@ namespace drawinglayer break; } } + + // get stroke + switch(pLineAttribute->getLineCap()) + { + default: /* com::sun::star::drawing::LineCap_BUTT */ + { + eCap = SvtGraphicStroke::capButt; + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + eCap = SvtGraphicStroke::capRound; + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + eCap = SvtGraphicStroke::capSquare; + break; + } + } } if(pStrokeAttribute) @@ -482,7 +503,7 @@ namespace drawinglayer PolyPolygon(aEndArrow), mfCurrentUnifiedTransparence, fLineWidth, - SvtGraphicStroke::capButt, + eCap, eJoin, fMiterLength, aDashArray); @@ -1203,6 +1224,7 @@ namespace drawinglayer LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fDiscreteLineWidth)); aLineInfo.SetLineJoin(rLine.getLineJoin()); + aLineInfo.SetLineCap(rLine.getLineCap()); for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) { diff --git a/editeng/inc/editeng/unoprnms.hxx b/editeng/inc/editeng/unoprnms.hxx index 9379e1b0616f..f612182349da 100644 --- a/editeng/inc/editeng/unoprnms.hxx +++ b/editeng/inc/editeng/unoprnms.hxx @@ -92,6 +92,7 @@ #define UNO_NAME_LINESTARTCENTER "LineStartCenter" #define UNO_NAME_LINEENDCENTER "LineEndCenter" #define UNO_NAME_LINETRANSPARENCE "LineTransparence" +#define UNO_NAME_LINECAP "LineCap" #define UNO_NAME_SHADOW "Shadow" #define UNO_NAME_SHADOWCOLOR "ShadowColor" diff --git a/filter/source/graphicfilter/eps/eps.cxx b/filter/source/graphicfilter/eps/eps.cxx index 2f63c27017d7..9e21611da409 100644 --- a/filter/source/graphicfilter/eps/eps.cxx +++ b/filter/source/graphicfilter/eps/eps.cxx @@ -2370,6 +2370,7 @@ void PSWriter::ImplWriteLineInfo( const LineInfo& rLineInfo ) l_aDashArray.push_back( 2 ); const double fLWidth(( ( rLineInfo.GetWidth() + 1 ) + ( rLineInfo.GetWidth() + 1 ) ) * 0.5); SvtGraphicStroke::JoinType aJoinType(SvtGraphicStroke::joinMiter); + SvtGraphicStroke::CapType aCapType(SvtGraphicStroke::capButt); switch(rLineInfo.GetLineJoin()) { @@ -2389,7 +2390,26 @@ void PSWriter::ImplWriteLineInfo( const LineInfo& rLineInfo ) break; } - ImplWriteLineInfo( fLWidth, fMiterLimit, SvtGraphicStroke::capButt, aJoinType, l_aDashArray ); + switch(rLineInfo.GetLineCap()) + { + default: /* com::sun::star::drawing::LineCap_BUTT */ + { + aCapType = SvtGraphicStroke::capButt; + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + aCapType = SvtGraphicStroke::capRound; + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + aCapType = SvtGraphicStroke::capSquare; + break; + } + } + + ImplWriteLineInfo( fLWidth, fMiterLimit, aCapType, aJoinType, l_aDashArray ); } //--------------------------------------------------------------------------------- diff --git a/filter/source/msfilter/escherex.cxx b/filter/source/msfilter/escherex.cxx index 5cc5f8a58477..f6e011fcf5e5 100644 --- a/filter/source/msfilter/escherex.cxx +++ b/filter/source/msfilter/escherex.cxx @@ -51,6 +51,7 @@ #include <com/sun/star/awt/Gradient.hpp> #include <com/sun/star/drawing/LineStyle.hpp> #include <com/sun/star/drawing/LineJoint.hpp> +#include <com/sun/star/drawing/LineCap.hpp> #include <com/sun/star/drawing/FillStyle.hpp> #include <com/sun/star/drawing/LineDash.hpp> #include <com/sun/star/drawing/BezierPoint.hpp> @@ -853,6 +854,35 @@ void EscherPropertyContainer::CreateLineProperties( AddOpt( ESCHER_Prop_lineEndArrowhead, eLineEnd ); nLineFlags |= 0x100010; } + + // support LineCaps + if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, String(RTL_CONSTASCII_USTRINGPARAM("LineCap")), sal_False)) + { + ::com::sun::star::drawing::LineCap aLineCap(com::sun::star::drawing::LineCap_BUTT); + + if(aAny >>= aLineCap) + { + switch (aLineCap) + { + default: /* com::sun::star::drawing::LineCap_BUTT */ + { + AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapFlat); + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapRound); + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapSquare); + break; + } + } + } + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, String( RTL_CONSTASCII_USTRINGPARAM( "LineStyle" ) ), sal_False ) ) { diff --git a/filter/source/msfilter/msdffimp.cxx b/filter/source/msfilter/msdffimp.cxx index 73d2320a6d98..ecebfd4b9f2a 100644 --- a/filter/source/msfilter/msdffimp.cxx +++ b/filter/source/msfilter/msdffimp.cxx @@ -920,19 +920,41 @@ void DffPropertyReader::ApplyLineAttributes( SfxItemSet& rSet, const MSO_SPT eSh // Linienattribute sal_Int32 nLineWidth = (sal_Int32)GetPropertyValue( DFF_Prop_lineWidth, 9525 ); + // support LineCap + const MSO_LineCap eLineCap((MSO_LineCap)GetPropertyValue(DFF_Prop_lineEndCapStyle, mso_lineEndCapSquare)); + + switch(eLineCap) + { + default: /* case mso_lineEndCapFlat */ + { + // no need to set, it is the default. If this changes, this needs to be activated + // rSet.Put(XLineCapItem(com::sun::star::drawing::LineCap_BUTT)); + break; + } + case mso_lineEndCapRound: + { + rSet.Put(XLineCapItem(com::sun::star::drawing::LineCap_ROUND)); + break; + } + case mso_lineEndCapSquare: + { + rSet.Put(XLineCapItem(com::sun::star::drawing::LineCap_SQUARE)); + break; + } + } + MSO_LineDashing eLineDashing = (MSO_LineDashing)GetPropertyValue( DFF_Prop_lineDashing, mso_lineSolid ); if ( eLineDashing == mso_lineSolid ) rSet.Put(XLineStyleItem( XLINE_SOLID ) ); else { -// MSO_LineCap eLineCap = (MSO_LineCap)GetPropertyValue( DFF_Prop_lineEndCapStyle, mso_lineEndCapSquare ); XDashStyle eDash = XDASH_RECT; sal_uInt16 nDots = 1; sal_uInt32 nDotLen = nLineWidth / 360; sal_uInt16 nDashes = 0; sal_uInt32 nDashLen = ( 8 * nLineWidth ) / 360; - sal_uInt32 nDistance = ( 3 * nLineWidth ) / 360;; + sal_uInt32 nDistance = ( 3 * nLineWidth ) / 360; switch ( eLineDashing ) { @@ -1049,24 +1071,27 @@ void DffPropertyReader::ApplyLineAttributes( SfxItemSet& rSet, const MSO_SPT eSh rSet.Put( XLineEndItem( aArrowName, basegfx::B2DPolyPolygon(aPoly) ) ); rSet.Put( XLineEndCenterItem( bArrowCenter ) ); } - if ( IsProperty( DFF_Prop_lineEndCapStyle ) ) - { - MSO_LineCap eLineCap = (MSO_LineCap)GetPropertyValue( DFF_Prop_lineEndCapStyle ); - const SfxPoolItem* pPoolItem = NULL; - if ( rSet.GetItemState( XATTR_LINEDASH, sal_False, &pPoolItem ) == SFX_ITEM_SET ) - { - XDashStyle eNewStyle = XDASH_RECT; - if ( eLineCap == mso_lineEndCapRound ) - eNewStyle = XDASH_ROUND; - const XDash& rOldDash = ( (const XLineDashItem*)pPoolItem )->GetDashValue(); - if ( rOldDash.GetDashStyle() != eNewStyle ) - { - XDash aNew( rOldDash ); - aNew.SetDashStyle( eNewStyle ); - rSet.Put( XLineDashItem( XubString(), aNew ) ); - } - } - } + + // this was used to at least adapt the lineDash to the lineCap before lineCap was + // supported, so with supporting lineCap this is no longer needed + //if ( IsProperty( DFF_Prop_lineEndCapStyle ) ) + //{ + // MSO_LineCap eLineCap = (MSO_LineCap)GetPropertyValue( DFF_Prop_lineEndCapStyle ); + // const SfxPoolItem* pPoolItem = NULL; + // if ( rSet.GetItemState( XATTR_LINEDASH, sal_False, &pPoolItem ) == SFX_ITEM_SET ) + // { + // XDashStyle eNewStyle = XDASH_RECT; + // if ( eLineCap == mso_lineEndCapRound ) + // eNewStyle = XDASH_ROUND; + // const XDash& rOldDash = ( (const XLineDashItem*)pPoolItem )->GetDashValue(); + // if ( rOldDash.GetDashStyle() != eNewStyle ) + // { + // XDash aNew( rOldDash ); + // aNew.SetDashStyle( eNewStyle ); + // rSet.Put( XLineDashItem( XubString(), aNew ) ); + // } + // } + //} } } else diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index ce85fca3df6c..5651a7f3d5c3 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -83,6 +83,10 @@ static const char aXMLAttrGradientUnits[] = "gradientUnits"; static const char aXMLAttrOffset[] = "offset"; static const char aXMLAttrStopColor[] = "stop-color"; +// added support for LineJoin and LineCap +static const char aXMLAttrStrokeLinejoin[] = "stroke-linejoin"; +static const char aXMLAttrStrokeLinecap[] = "stroke-linecap"; + // ----------------------------------------------------------------------------- static const sal_Unicode pBase64[] = @@ -674,6 +678,51 @@ void SVGActionWriter::ImplWriteShape( const SVGShapeDescriptor& rShape, sal_Bool mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStrokeWidth, ::rtl::OUString::valueOf( nStrokeWidth ) ); } + // support for LineJoin + switch(rShape.maLineJoin) + { + default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE + case basegfx::B2DLINEJOIN_MITER: + { + // miter is Svg default, so no need to write until the exporter might write styles. + // If this happens, activate here + // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, ::rtl::OUString::createFromAscii("miter")); + break; + } + case basegfx::B2DLINEJOIN_BEVEL: + { + mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, ::rtl::OUString::createFromAscii("bevel")); + break; + } + case basegfx::B2DLINEJOIN_ROUND: + { + mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, ::rtl::OUString::createFromAscii("round")); + break; + } + } + + // support for LineCap + switch(rShape.maLineCap) + { + default: /* com::sun::star::drawing::LineCap_BUTT */ + { + // butt is Svg default, so no need to write until the exporter might write styles. + // If this happens, activate here + // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, ::rtl::OUString::createFromAscii("butt")); + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, ::rtl::OUString::createFromAscii("round")); + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, ::rtl::OUString::createFromAscii("square")); + break; + } + } + if( rShape.maDashArray.size() ) { const ::rtl::OUString aComma( B2UCONST( "," ) ); @@ -1486,6 +1535,46 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, mapCurShape->maShapeLineColor.SetTransparency( (sal_uInt8) FRound( aStroke.getTransparency() * 255.0 ) ); mapCurShape->mnStrokeWidth = FRound( aStroke.getStrokeWidth() ); aStroke.getDashArray( mapCurShape->maDashArray ); + + // added support for LineJoin + switch(aStroke.getJoinType()) + { + default: /* SvtGraphicStroke::joinMiter, SvtGraphicStroke::joinNone */ + { + mapCurShape->maLineJoin = basegfx::B2DLINEJOIN_MITER; + break; + } + case SvtGraphicStroke::joinRound: + { + mapCurShape->maLineJoin = basegfx::B2DLINEJOIN_ROUND; + break; + } + case SvtGraphicStroke::joinBevel: + { + mapCurShape->maLineJoin = basegfx::B2DLINEJOIN_BEVEL; + break; + } + } + + // added support for LineCap + switch(aStroke.getCapType()) + { + default: /* SvtGraphicStroke::capButt */ + { + mapCurShape->maLineCap = com::sun::star::drawing::LineCap_BUTT; + break; + } + case SvtGraphicStroke::capRound: + { + mapCurShape->maLineCap = com::sun::star::drawing::LineCap_ROUND; + break; + } + case SvtGraphicStroke::capSquare: + { + mapCurShape->maLineCap = com::sun::star::drawing::LineCap_SQUARE; + break; + } + } } // write open shape in every case diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx index 233a244e5cd4..e00b43062ded 100644 --- a/filter/source/svg/svgwriter.hxx +++ b/filter/source/svg/svgwriter.hxx @@ -137,20 +137,26 @@ public: struct SVGShapeDescriptor { - PolyPolygon maShapePolyPoly; - Color maShapeFillColor; - Color maShapeLineColor; - sal_Int32 mnStrokeWidth; - SvtGraphicStroke::DashArray maDashArray; - ::std::auto_ptr< Gradient > mapShapeGradient; - ::rtl::OUString maId; + PolyPolygon maShapePolyPoly; + Color maShapeFillColor; + Color maShapeLineColor; + sal_Int32 mnStrokeWidth; + SvtGraphicStroke::DashArray maDashArray; + ::std::auto_ptr< Gradient > mapShapeGradient; + ::rtl::OUString maId; + + // added support for LineJoin and LineCap + basegfx::B2DLineJoin maLineJoin; + com::sun::star::drawing::LineCap maLineCap; // ------------------------------------------------------------------------- SVGShapeDescriptor() : maShapeFillColor( Color( COL_TRANSPARENT ) ), maShapeLineColor( Color( COL_TRANSPARENT ) ), - mnStrokeWidth( 0 ) + mnStrokeWidth( 0 ), + maLineJoin(basegfx::B2DLINEJOIN_MITER), // miter is Svg 'stroke-linejoin' default + maLineCap(com::sun::star::drawing::LineCap_BUTT) // butt is Svg 'stroke-linecap' default { } }; diff --git a/offapi/com/sun/star/drawing/LineCap.idl b/offapi/com/sun/star/drawing/LineCap.idl new file mode 100644 index 000000000000..ba825356407b --- /dev/null +++ b/offapi/com/sun/star/drawing/LineCap.idl @@ -0,0 +1,58 @@ +/************************************************************** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + +#ifndef __com_sun_star_drawing_LineCap_idl__ +#define __com_sun_star_drawing_LineCap_idl__ + +//============================================================================= + + module com { module sun { module star { module drawing { + +//============================================================================= + +// DocMerge from xml: enum com::sun::star::drawing::LineCap +/** The <type>LineCap</type> defines rendering of ends of thick lines + */ +published enum LineCap +{ + + // DocMerge from xml: value com::sun::star::drawing::LineCap::BUTT + /** the line will end without any additional shape + */ + BUTT, + + // DocMerge from xml: value com::sun::star::drawing::LineCap::ROUND + /** the line will get a half cirle as additional cap + */ + ROUND, + + // DocMerge from xml: value com::sun::star::drawing::LineCap::SQUARE + /** the line will get a half square as additional cap + */ + SQUARE +}; + +//============================================================================= + +}; }; }; }; + +#endif + diff --git a/offapi/com/sun/star/drawing/LineProperties.idl b/offapi/com/sun/star/drawing/LineProperties.idl index 9e7cbc992444..af14fa365eaa 100644 --- a/offapi/com/sun/star/drawing/LineProperties.idl +++ b/offapi/com/sun/star/drawing/LineProperties.idl @@ -23,26 +23,12 @@ #ifndef __com_sun_star_drawing_LineProperties_idl__ #define __com_sun_star_drawing_LineProperties_idl__ -#ifndef __com_sun_star_drawing_LineStyle_idl__ #include <com/sun/star/drawing/LineStyle.idl> -#endif - -#ifndef __com_sun_star_util_Color_idl__ #include <com/sun/star/util/Color.idl> -#endif - -#ifndef __com_sun_star_drawing_LineDash_idl__ #include <com/sun/star/drawing/LineDash.idl> -#endif - -#ifndef __com_sun_star_drawing_PolyPolygonBezierCoords_idl__ #include <com/sun/star/drawing/PolyPolygonBezierCoords.idl> -#endif - -#ifndef __com_sun_star_drawing_LineJoint_idl__ #include <com/sun/star/drawing/LineJoint.idl> -#endif - +#include <com/sun/star/drawing/LineCap.idl> //============================================================================= @@ -98,6 +84,12 @@ published service LineProperties //------------------------------------------------------------------------- + /** This property defines the rendering of ends of thick lines + */ + [optional, property] com::sun::star::drawing::LineCap LineCap; + + //------------------------------------------------------------------------- + /** This property contains the name of the line start poly polygon bezier. <p>If this string is empty, no line start polygon is rendered. */ diff --git a/offapi/com/sun/star/drawing/makefile.mk b/offapi/com/sun/star/drawing/makefile.mk index 47f97f950b66..6c7a23fc747d 100644 --- a/offapi/com/sun/star/drawing/makefile.mk +++ b/offapi/com/sun/star/drawing/makefile.mk @@ -120,6 +120,7 @@ IDLFILES=\ Layer.idl\ LayerManager.idl\ LayerType.idl\ + LineCap.idl\ LineDash.idl\ LineEndType.idl\ LineJoint.idl\ diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx index d43a9fbad55d..b8b6c369a501 100644 --- a/svgio/source/svgreader/svgstyleattributes.cxx +++ b/svgio/source/svgreader/svgstyleattributes.cxx @@ -63,6 +63,28 @@ namespace svgio return basegfx::B2DLINEJOIN_MITER; } + com::sun::star::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap) + { + switch(aStrokeLinecap) + { + default: /* StrokeLinecap_notset, StrokeLinecap_butt */ + { + return com::sun::star::drawing::LineCap_BUTT; + break; + } + case StrokeLinecap_round: + { + return com::sun::star::drawing::LineCap_ROUND; + break; + } + case StrokeLinecap_square: + { + return com::sun::star::drawing::LineCap_SQUARE; + break; + } + } + } + FontStretch getWider(FontStretch aSource) { switch(aSource) @@ -658,8 +680,9 @@ namespace svgio if(basegfx::fTools::more(fStrokeWidth, 0.0)) { - // get LineJoin and stroke array + // get LineJoin, LineCap and stroke array const basegfx::B2DLineJoin aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin())); + const com::sun::star::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap())); ::std::vector< double > aDashArray; if(!getStrokeDasharray().empty()) @@ -668,14 +691,14 @@ namespace svgio } // todo: Handle getStrokeDashOffset() - // todo: Handle getStrokeLinecap() // prepare line attribute drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive; const drawinglayer::attribute::LineAttribute aLineAttribute( pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0), fStrokeWidth, - aB2DLineJoin); + aB2DLineJoin, + aLineCap); if(aDashArray.empty()) { diff --git a/svx/Package_inc.mk b/svx/Package_inc.mk index 6550b70493a5..d157384731b7 100644 --- a/svx/Package_inc.mk +++ b/svx/Package_inc.mk @@ -172,6 +172,7 @@ $(eval $(call gb_Package_add_file,svx_inc,inc/svx/dbexch.hrc,svx/dbexch.hrc)) $(eval $(call gb_Package_add_file,svx_inc,inc/svx/unomaster.hxx,svx/unomaster.hxx)) $(eval $(call gb_Package_add_file,svx_inc,inc/svx/svdedtv.hxx,svx/svdedtv.hxx)) $(eval $(call gb_Package_add_file,svx_inc,inc/svx/xlinjoit.hxx,svx/xlinjoit.hxx)) +$(eval $(call gb_Package_add_file,svx_inc,inc/svx/xlncapit.hxx,svx/xlncapit.hxx)) $(eval $(call gb_Package_add_file,svx_inc,inc/svx/sxmbritm.hxx,svx/sxmbritm.hxx)) $(eval $(call gb_Package_add_file,svx_inc,inc/svx/AccessibleGraphicShape.hxx,svx/AccessibleGraphicShape.hxx)) $(eval $(call gb_Package_add_file,svx_inc,inc/svx/xlnstit.hxx,svx/xlnstit.hxx)) diff --git a/svx/inc/svx/dialogs.hrc b/svx/inc/svx/dialogs.hrc index 9df4d1638d29..0310b8a32791 100755 --- a/svx/inc/svx/dialogs.hrc +++ b/svx/inc/svx/dialogs.hrc @@ -633,6 +633,11 @@ #define RID_SVXSTR_TBLAFMT_YELLOW (RID_SVX_START + 575) #define RID_SVXSTR_TBLAFMT_END (RID_SVX_START + 576) +// string resources for XLineCap item +#define RID_SVXSTR_LINECAP_BUTT (RID_SVX_START + 586 ) +#define RID_SVXSTR_LINECAP_ROUND (RID_SVX_START + 587 ) +#define RID_SVXSTR_LINECAP_SQUARE (RID_SVX_START + 588 ) + // string resources for XLineJoint item #define RID_SVXSTR_LINEJOINT_NONE RID_SVXSTR_NONE #define RID_SVXSTR_LINEJOINT_MIDDLE (RID_SVX_START + 589 ) diff --git a/svx/inc/svx/unoshprp.hxx b/svx/inc/svx/unoshprp.hxx index 13c265a29365..5c31a0aaa8f4 100644 --- a/svx/inc/svx/unoshprp.hxx +++ b/svx/inc/svx/unoshprp.hxx @@ -32,6 +32,7 @@ #include <com/sun/star/awt/Gradient.hpp> #include <com/sun/star/drawing/Hatch.hpp> #include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/LineCap.hpp> #include <com/sun/star/drawing/LineDash.hpp> #include <com/sun/star/drawing/LineJoint.hpp> #include <com/sun/star/drawing/LineStyle.hpp> @@ -211,6 +212,7 @@ { MAP_CHAR_LEN(UNO_NAME_SHADOWYDIST), SDRATTR_SHADOWYDIST, &::getCppuType((const sal_Int32*)0), 0, SFX_METRIC_ITEM}, #define LINE_PROPERTIES_DEFAULTS\ + { MAP_CHAR_LEN(UNO_NAME_LINECAP), XATTR_LINECAP, &::getCppuType((const ::com::sun::star::drawing::LineCap*)0), 0, 0}, \ { MAP_CHAR_LEN(UNO_NAME_LINECOLOR), XATTR_LINECOLOR, &::getCppuType((const sal_Int32*)0) , 0, 0}, \ { MAP_CHAR_LEN(UNO_NAME_LINEENDCENTER), XATTR_LINEENDCENTER, &::getBooleanCppuType() , 0, 0}, \ { MAP_CHAR_LEN(UNO_NAME_LINEENDWIDTH), XATTR_LINEENDWIDTH, &::getCppuType((const sal_Int32*)0) , 0, SFX_METRIC_ITEM}, \ diff --git a/svx/inc/svx/xattr.hxx b/svx/inc/svx/xattr.hxx index b85efde692b7..6dea0ad2cbcf 100644 --- a/svx/inc/svx/xattr.hxx +++ b/svx/inc/svx/xattr.hxx @@ -57,6 +57,7 @@ class XGradientTable; #include <svx/xtextit0.hxx> #include <svx/xsetit.hxx> #include <svx/xlinjoit.hxx> +#include <svx/xlncapit.hxx> #endif // _XATTR_HXX diff --git a/svx/inc/svx/xdef.hxx b/svx/inc/svx/xdef.hxx index e6a26669323e..099542c573b4 100644 --- a/svx/inc/svx/xdef.hxx +++ b/svx/inc/svx/xdef.hxx @@ -50,7 +50,8 @@ #define XATTR_LINEENDCENTER (XATTR_LINE_FIRST + 9) /* V3: 1009 V2: 1009 */ #define XATTR_LINETRANSPARENCE (XATTR_LINE_FIRST + 10) /* V3: 1010 V2: 1010 */ #define XATTR_LINEJOINT (XATTR_LINE_FIRST + 11) /* V3: 1011 V2: 1011 */ -#define XATTR_LINE_LAST XATTR_LINEJOINT +#define XATTR_LINECAP (XATTR_LINE_FIRST + 12) /* V3: 1012 */ +#define XATTR_LINE_LAST XATTR_LINECAP #define XATTRSET_LINE (XATTR_LINE_LAST + 1) /* V3: 1017 V2: 1017 */ #define XATTR_FILL_FIRST (XATTRSET_LINE + 1) /* V3: 1018 V2: 1018 */ diff --git a/svx/inc/svx/xlncapit.hxx b/svx/inc/svx/xlncapit.hxx new file mode 100644 index 000000000000..1949e9fda419 --- /dev/null +++ b/svx/inc/svx/xlncapit.hxx @@ -0,0 +1,57 @@ +/************************************************************** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + +#ifndef _SVX_XLNCAPIT_HXX +#define _SVX_XLNCAPIT_HXX + +#include <svx/svxdllapi.h> +#include <svl/eitem.hxx> +#include <svx/xenum.hxx> +#include <com/sun/star/drawing/LineCap.hpp> + +//--------------------- +// class LineStyleItem +//--------------------- + +class SVX_DLLPUBLIC XLineCapItem : public SfxEnumItem +{ +public: + TYPEINFO(); + XLineCapItem(com::sun::star::drawing::LineCap eLineCap = com::sun::star::drawing::LineCap_BUTT); + XLineCapItem(SvStream& rIn); + + virtual sal_uInt16 GetVersion( sal_uInt16 nFileFormatVersion ) const; + virtual SfxPoolItem* Clone( SfxItemPool* pPool = 0 ) const; + virtual SfxPoolItem* Create( SvStream& rIn, sal_uInt16 nVer ) const; + + virtual sal_Bool QueryValue( com::sun::star::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const; + virtual sal_Bool PutValue( const com::sun::star::uno::Any& rVal, sal_uInt8 nMemberId = 0 ); + virtual SfxItemPresentation GetPresentation( SfxItemPresentation ePres, + SfxMapUnit eCoreMetric, SfxMapUnit ePresMetric, + String &rText, const IntlWrapper * = 0 ) const; + + virtual sal_uInt16 GetValueCount() const; + com::sun::star::drawing::LineCap GetValue() const; +}; + +#endif // _SVX_XLNCAPIT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/sdstring.src b/svx/source/dialog/sdstring.src index b53fcbea056a..319689163e73 100644 --- a/svx/source/dialog/sdstring.src +++ b/svx/source/dialog/sdstring.src @@ -359,6 +359,19 @@ String RID_SVXSTR_LINEJOINT_ROUND { Text [ en-US ] = "Line joint round"; }; +String RID_SVXSTR_LINECAP_BUTT +{ + Text [ en-US ] = "Line cap flat"; // string as in Excel +}; +String RID_SVXSTR_LINECAP_ROUND +{ + Text [ en-US ] = "Line cap round"; +}; +String RID_SVXSTR_LINECAP_SQUARE +{ + Text [ en-US ] = "Line cap square"; +}; + /////////////////////////////////////////////////////////////////////////////// // diff --git a/svx/source/sdr/attribute/sdrformtextattribute.cxx b/svx/source/sdr/attribute/sdrformtextattribute.cxx index d128d7f8a1da..1608b9524391 100644 --- a/svx/source/sdr/attribute/sdrformtextattribute.cxx +++ b/svx/source/sdr/attribute/sdrformtextattribute.cxx @@ -42,12 +42,14 @@ #include <svx/xlnclit.hxx> #include <svx/xlnwtit.hxx> #include <svx/xlinjoit.hxx> +#include <svx/xlncapit.hxx> #include <svx/xlineit0.hxx> #include <svx/xdash.hxx> #include <svx/xlndsit.hxx> #include <drawinglayer/attribute/lineattribute.hxx> #include <drawinglayer/attribute/strokeattribute.hxx> #include <svx/sdr/attribute/sdrformtextoutlineattribute.hxx> +#include <com/sun/star/drawing/LineCap.hpp> ////////////////////////////////////////////////////////////////////////////// // helper to get line, stroke and transparence attributes from SfxItemSet @@ -114,8 +116,13 @@ namespace const sal_uInt32 nLineWidth = ((const XLineWidthItem&)(rSet.Get(XATTR_LINEWIDTH))).GetValue(); const XLineJoint eLineJoint = ((const XLineJointItem&)(rSet.Get(XATTR_LINEJOINT))).GetValue(); + const com::sun::star::drawing::LineCap eLineCap = ((const XLineCapItem&)(rSet.Get(XATTR_LINECAP))).GetValue(); - return drawinglayer::attribute::LineAttribute(aColorAttribute, (double)nLineWidth, impGetB2DLineJoin(eLineJoint)); + return drawinglayer::attribute::LineAttribute( + aColorAttribute, + (double)nLineWidth, + impGetB2DLineJoin(eLineJoint), + eLineCap); } drawinglayer::attribute::StrokeAttribute impGetStrokeAttribute(const SfxItemSet& rSet) diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx index 11d64679b738..40a42e74849a 100644 --- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx +++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx @@ -31,6 +31,7 @@ #include <svx/xlntrit.hxx> #include <svx/xlnwtit.hxx> #include <svx/xlinjoit.hxx> +#include <svx/xlncapit.hxx> #include <svx/xlnclit.hxx> #include <svx/xlnstwit.hxx> #include <svx/xlnedwit.hxx> @@ -78,6 +79,7 @@ #include <drawinglayer/attribute/sdrlightingattribute3d.hxx> #include <drawinglayer/attribute/sdrlightattribute3d.hxx> #include <svx/sdr/attribute/sdrfilltextattribute.hxx> +#include <com/sun/star/drawing/LineCap.hpp> ////////////////////////////////////////////////////////////////////////////// @@ -237,6 +239,7 @@ namespace drawinglayer const sal_uInt32 nWidth(((const XLineWidthItem&)(rSet.Get(XATTR_LINEWIDTH))).GetValue()); const Color aColor(((const XLineColorItem&)(rSet.Get(XATTR_LINECOLOR))).GetColorValue()); const XLineJoint eJoint(((const XLineJointItem&)(rSet.Get(XATTR_LINEJOINT))).GetValue()); + const com::sun::star::drawing::LineCap eCap(((const XLineCapItem&)(rSet.Get(XATTR_LINECAP))).GetValue()); ::std::vector< double > aDotDashArray; double fFullDotDashLen(0.0); @@ -255,6 +258,7 @@ namespace drawinglayer (double)nWidth, (double)nTransparence * 0.01, aColor.getBColor(), + eCap, aDotDashArray, fFullDotDashLen); } diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index 19cadccbd15e..3b58c04d5717 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -129,7 +129,7 @@ namespace drawinglayer aScaledPolygon.transform(rObjectTransform); // create line and stroke attribute - const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin()); + const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap()); const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen()); BasePrimitive2D* pNewLinePrimitive = 0L; diff --git a/svx/source/svdraw/svdfmtf.cxx b/svx/source/svdraw/svdfmtf.cxx index 5519973e0c88..6156e7868f01 100644 --- a/svx/source/svdraw/svdfmtf.cxx +++ b/svx/source/svdraw/svdfmtf.cxx @@ -37,6 +37,7 @@ #include <editeng/crsditem.hxx> #include <editeng/shdditem.hxx> #include <svx/xlnclit.hxx> +#include <svx/xlncapit.hxx> #include <svx/xlnwtit.hxx> #include <svx/xflclit.hxx> #include <svx/xgrad.hxx> @@ -76,6 +77,7 @@ ImpSdrGDIMetaFileImport::ImpSdrGDIMetaFileImport(SdrModel& rModel): pPage(NULL),pModel(NULL),nLayer(0), nLineWidth(0), maLineJoin(basegfx::B2DLINEJOIN_NONE), + maLineCap(com::sun::star::drawing::LineCap_BUTT), maDash(XDASH_RECT, 0, 0, 0, 0, 0), bFntDirty(sal_True), bLastObjWasPolyWithoutLine(sal_False),bNoLine(sal_False),bNoFill(sal_False),bLastObjWasLine(sal_False) @@ -305,6 +307,9 @@ void ImpSdrGDIMetaFileImport::SetAttributes(SdrObject* pObj, FASTBOOL bForceText break; } + // Add LineCap support + pLineAttr->Put(XLineCapItem(maLineCap)); + if(((maDash.GetDots() && maDash.GetDotLen()) || (maDash.GetDashes() && maDash.GetDashLen())) && maDash.GetDistance()) { pLineAttr->Put(XLineDashItem(String(), maDash)); @@ -480,6 +485,7 @@ void ImpSdrGDIMetaFileImport::DoAction(MetaLineAction& rAct) SdrPathObj* pPath = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aLine)); nLineWidth = nNewLineWidth; maLineJoin = rLineInfo.GetLineJoin(); + maLineCap = rLineInfo.GetLineCap(); maDash = XDash(XDASH_RECT, rLineInfo.GetDotCount(), rLineInfo.GetDotLen(), rLineInfo.GetDashCount(), rLineInfo.GetDashLen(), @@ -685,6 +691,7 @@ void ImpSdrGDIMetaFileImport::DoAction( MetaPolyLineAction& rAct ) basegfx::B2DPolyPolygon(aSource)); nLineWidth = nNewLineWidth; maLineJoin = rLineInfo.GetLineJoin(); + maLineCap = rLineInfo.GetLineCap(); maDash = XDash(XDASH_RECT, rLineInfo.GetDotCount(), rLineInfo.GetDotLen(), rLineInfo.GetDashCount(), rLineInfo.GetDashLen(), diff --git a/svx/source/svdraw/svdfmtf.hxx b/svx/source/svdraw/svdfmtf.hxx index c311a8d5dc2f..0d3f23a693b0 100644 --- a/svx/source/svdraw/svdfmtf.hxx +++ b/svx/source/svdraw/svdfmtf.hxx @@ -80,6 +80,7 @@ protected: Color aOldLineColor; sal_Int32 nLineWidth; basegfx::B2DLineJoin maLineJoin; + com::sun::star::drawing::LineCap maLineCap; XDash maDash; sal_Bool bMov; diff --git a/svx/source/xoutdev/xattr2.cxx b/svx/source/xoutdev/xattr2.cxx index 28b22f08e6e0..33ac5abb2835 100644 --- a/svx/source/xoutdev/xattr2.cxx +++ b/svx/source/xoutdev/xattr2.cxx @@ -28,6 +28,7 @@ #include <com/sun/star/drawing/LineJoint.hpp> +#include <com/sun/star/drawing/LineCap.hpp> #include <com/sun/star/uno/Any.hxx> #include <svx/dialogs.hrc> @@ -309,6 +310,150 @@ sal_uInt16 XLineJointItem::GetValueCount() const return 5; } +//----------------------- +// class XLineCapItem - +//----------------------- + +TYPEINIT1_AUTOFACTORY(XLineCapItem, SfxEnumItem); + +// ----------------------------------------------------------------------------- + +XLineCapItem::XLineCapItem(com::sun::star::drawing::LineCap eLineCap) +: SfxEnumItem(XATTR_LINECAP, sal::static_int_cast< sal_uInt16 >(eLineCap)) +{ +} + +// ----------------------------------------------------------------------------- + +XLineCapItem::XLineCapItem( SvStream& rIn ) +: SfxEnumItem(XATTR_LINECAP, rIn) +{ +} + +// ----------------------------------------------------------------------------- + +sal_uInt16 XLineCapItem::GetVersion( sal_uInt16 /*nFileFormatVersion*/) const +{ + return 1; +} + +// ----------------------------------------------------------------------------- + +SfxPoolItem* XLineCapItem::Create( SvStream& rIn, sal_uInt16 nVer ) const +{ + XLineCapItem* pRet = new XLineCapItem( rIn ); + + if(nVer < 1) + pRet->SetValue(com::sun::star::drawing::LineCap_BUTT); + + return pRet; +} + +// ----------------------------------------------------------------------------- + +SfxPoolItem* XLineCapItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new XLineCapItem( *this ); +} + +// ----------------------------------------------------------------------------- + +SfxItemPresentation XLineCapItem::GetPresentation( SfxItemPresentation ePres, SfxMapUnit /*eCoreUnit*/, + SfxMapUnit /*ePresUnit*/, XubString& rText, const IntlWrapper*) const +{ + rText.Erase(); + + switch( ePres ) + { + case SFX_ITEM_PRESENTATION_NONE: return ePres; + + case SFX_ITEM_PRESENTATION_COMPLETE: + case SFX_ITEM_PRESENTATION_NAMELESS: + { + sal_uInt16 nId = 0; + + switch( GetValue() ) + { + default: /*com::sun::star::drawing::LineCap_BUTT*/ + nId = RID_SVXSTR_LINECAP_BUTT; + break; + + case(com::sun::star::drawing::LineCap_ROUND): + nId = RID_SVXSTR_LINECAP_ROUND; + break; + + case(com::sun::star::drawing::LineCap_SQUARE): + nId = RID_SVXSTR_LINECAP_SQUARE; + break; + } + + if( nId ) + rText = SVX_RESSTR( nId ); + + return ePres; + } + default: + return SFX_ITEM_PRESENTATION_NONE; + } +} + +// ----------------------------------------------------------------------------- + +sal_Bool XLineCapItem::QueryValue( ::com::sun::star::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + const com::sun::star::drawing::LineCap eCap(GetValue()); + rVal <<= eCap; + return true; +} + +// ----------------------------------------------------------------------------- + +sal_Bool XLineCapItem::PutValue( const ::com::sun::star::uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + com::sun::star::drawing::LineCap eUnoCap; + + if(!(rVal >>= eUnoCap)) + { + // also try an int (for Basic) + sal_Int32 nLJ(0); + + if(!(rVal >>= nLJ)) + { + return false; + } + + eUnoCap = (com::sun::star::drawing::LineCap)nLJ; + } + + OSL_ENSURE(com::sun::star::drawing::LineCap_BUTT == eUnoCap + || com::sun::star::drawing::LineCap_ROUND == eUnoCap + || com::sun::star::drawing::LineCap_SQUARE == eUnoCap, "Unknown enum value in XATTR_LINECAP (!)"); + + SetValue(sal::static_int_cast< sal_uInt16 >(eUnoCap)); + + return true; +} + +// ----------------------------------------------------------------------------- + +sal_uInt16 XLineCapItem::GetValueCount() const +{ + // don't forget to update the api interface also + return 3; +} + +// ----------------------------------------------------------------------------- + +com::sun::star::drawing::LineCap XLineCapItem::GetValue() const +{ + const com::sun::star::drawing::LineCap eRetval((com::sun::star::drawing::LineCap)SfxEnumItem::GetValue()); + OSL_ENSURE(com::sun::star::drawing::LineCap_BUTT == eRetval + || com::sun::star::drawing::LineCap_ROUND == eRetval + || com::sun::star::drawing::LineCap_SQUARE == eRetval, "Unknown enum value in XATTR_LINECAP (!)"); + + return (com::sun::star::drawing::LineCap)SfxEnumItem::GetValue(); +} + //------------------------------ // class XFillTransparenceItem //------------------------------ diff --git a/svx/source/xoutdev/xpool.cxx b/svx/source/xoutdev/xpool.cxx index ab3de0a3659e..f1c8ac9f1ed5 100644 --- a/svx/source/xoutdev/xpool.cxx +++ b/svx/source/xoutdev/xpool.cxx @@ -87,6 +87,7 @@ XOutdevItemPool::XOutdevItemPool( mppLocalPoolDefaults[XATTR_LINEENDCENTER -XATTR_START] = new XLineEndCenterItem; mppLocalPoolDefaults[XATTR_LINETRANSPARENCE -XATTR_START] = new XLineTransparenceItem; mppLocalPoolDefaults[XATTR_LINEJOINT -XATTR_START] = new XLineJointItem; + mppLocalPoolDefaults[XATTR_LINECAP -XATTR_START] = new XLineCapItem; mppLocalPoolDefaults[XATTR_FILLSTYLE -XATTR_START] = new XFillStyleItem; mppLocalPoolDefaults[XATTR_FILLCOLOR -XATTR_START] = new XFillColorItem (aNullStr,aNullFillCol); mppLocalPoolDefaults[XATTR_FILLGRADIENT -XATTR_START] = new XFillGradientItem(this,aNullGrad); diff --git a/vcl/aqua/source/gdi/salgdi.cxx b/vcl/aqua/source/gdi/salgdi.cxx index 444436cad5d9..b2652ac17437 100755 --- a/vcl/aqua/source/gdi/salgdi.cxx +++ b/vcl/aqua/source/gdi/salgdi.cxx @@ -980,10 +980,12 @@ bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPol // ----------------------------------------------------------------------- -bool AquaSalGraphics::drawPolyLine( const ::basegfx::B2DPolygon& rPolyLine, +bool AquaSalGraphics::drawPolyLine( + const ::basegfx::B2DPolygon& rPolyLine, double fTransparency, const ::basegfx::B2DVector& rLineWidths, - basegfx::B2DLineJoin eLineJoin ) + basegfx::B2DLineJoin eLineJoin, + com::sun::star::drawing::LineCap eLineCap) { // short circuit if there is nothing to do const int nPointCount = rPolyLine.count(); diff --git a/vcl/inc/aqua/salgdi.h b/vcl/inc/aqua/salgdi.h index 7d53df93a46c..8cc6dd7ba6fd 100644 --- a/vcl/inc/aqua/salgdi.h +++ b/vcl/inc/aqua/salgdi.h @@ -186,7 +186,12 @@ public: virtual sal_Bool drawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); virtual sal_Bool drawPolygonBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); virtual sal_Bool drawPolyPolygonBezier( sal_uLong nPoly, const sal_uLong* pPoints, const SalPoint* const* pPtAry, const sal_uInt8* const* pFlgAry ); - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap eLineCap); // CopyArea --> No RasterOp, but ClipRegion virtual void copyArea( long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth, diff --git a/vcl/inc/os2/salgdi.h b/vcl/inc/os2/salgdi.h index de95e5b2ec41..7a5c475adec0 100644 --- a/vcl/inc/os2/salgdi.h +++ b/vcl/inc/os2/salgdi.h @@ -166,7 +166,12 @@ protected: virtual void drawPolygon( ULONG nPoints, const SalPoint* pPtAry ); virtual void drawPolyPolygon( ULONG nPoly, const ULONG* pPoints, PCONSTSALPOINT* pPtAry ); virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidth, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap); virtual sal_Bool drawPolyLineBezier( sal_uIntPtr nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); virtual sal_Bool drawPolygonBezier( sal_uIntPtr nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); virtual sal_Bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const sal_uInt8* const* pFlgAry ); diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx index 7e9feaf80706..72720845a307 100755 --- a/vcl/inc/salgdi.hxx +++ b/vcl/inc/salgdi.hxx @@ -119,7 +119,12 @@ protected: virtual void drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry ) = 0; virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) = 0; virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ) = 0; - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin ) = 0; + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap) = 0; virtual sal_Bool drawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) = 0; virtual sal_Bool drawPolygonBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) = 0; virtual sal_Bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const sal_uInt8* const* pFlgAry ) = 0; @@ -361,7 +366,15 @@ public: PCONSTSALPOINT* pPtAry, const OutputDevice *pOutDev ); bool DrawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency, const OutputDevice* ); - bool DrawPolyLine( const basegfx::B2DPolygon&, double fTransparency, const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin, const OutputDevice* ); + + bool DrawPolyLine( + const basegfx::B2DPolygon& i_rPolygon, + double i_fTransparency, + const basegfx::B2DVector& i_rLineWidth, + basegfx::B2DLineJoin i_eLineJoin, + com::sun::star::drawing::LineCap i_eLineCap, + const OutputDevice* i_pOutDev); + sal_Bool DrawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry, diff --git a/vcl/inc/unx/pspgraphics.h b/vcl/inc/unx/pspgraphics.h index a534611d0fa9..3323a6e85d0a 100644 --- a/vcl/inc/unx/pspgraphics.h +++ b/vcl/inc/unx/pspgraphics.h @@ -136,7 +136,12 @@ public: const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ); virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); - virtual bool drawPolyLine( const basegfx::B2DPolygon&, double fTransparency, const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin); + virtual bool drawPolyLine( + const basegfx::B2DPolygon&, + double fTransparency, + const basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap); virtual sal_Bool drawPolyLineBezier( sal_uIntPtr nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h index c69e4b0630c4..60a734be51b2 100644 --- a/vcl/inc/unx/salgdi.h +++ b/vcl/inc/unx/salgdi.h @@ -284,7 +284,12 @@ public: const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ); virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidth, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap); virtual bool drawFilledTrapezoids( const ::basegfx::B2DTrapezoid*, int nTrapCount, double fTransparency ); #if 1 // TODO: remove these obselete methods diff --git a/vcl/inc/vcl/cvtsvm.hxx b/vcl/inc/vcl/cvtsvm.hxx index 80ae55056a54..07a565857f9f 100644 --- a/vcl/inc/vcl/cvtsvm.hxx +++ b/vcl/inc/vcl/cvtsvm.hxx @@ -82,6 +82,9 @@ #define GDI_EXTENDEDPOLYGON_ACTION 1034 #define GDI_LINEDASHDOT_ACTION 1035 +// Added LineCap support +#define GDI_LINECAP_ACTION 1036 + // ---------------- // - SVMConverter - // ---------------- diff --git a/vcl/inc/vcl/lineinfo.hxx b/vcl/inc/vcl/lineinfo.hxx index 6bf56af43258..13f2a9f2a19e 100644 --- a/vcl/inc/vcl/lineinfo.hxx +++ b/vcl/inc/vcl/lineinfo.hxx @@ -28,6 +28,7 @@ #include <tools/gen.hxx> #include <vcl/vclenum.hxx> #include <basegfx/vector/b2enums.hxx> +#include <com/sun/star/drawing/LineCap.hpp> // ---------------- // - ImplLineInfo - @@ -48,6 +49,7 @@ struct ImplLineInfo long mnDistance; basegfx::B2DLineJoin meLineJoin; + com::sun::star::drawing::LineCap meLineCap; ImplLineInfo(); ImplLineInfo( const ImplLineInfo& rImplLineInfo ); @@ -108,7 +110,10 @@ public: void SetLineJoin(basegfx::B2DLineJoin eLineJoin); basegfx::B2DLineJoin GetLineJoin() const { return mpImplLineInfo->meLineJoin; } - sal_Bool IsDefault() const { return( !mpImplLineInfo->mnWidth && ( LINE_SOLID == mpImplLineInfo->meStyle ) ); } + void SetLineCap(com::sun::star::drawing::LineCap eLineCap); + com::sun::star::drawing::LineCap GetLineCap() const { return mpImplLineInfo->meLineCap; } + + sal_Bool IsDefault() const; friend VCL_DLLPUBLIC SvStream& operator>>( SvStream& rIStm, LineInfo& rLineInfo ); friend VCL_DLLPUBLIC SvStream& operator<<( SvStream& rOStm, const LineInfo& rLineInfo ); diff --git a/vcl/inc/vcl/outdev.hxx b/vcl/inc/vcl/outdev.hxx index 30d8c0f3eaa0..8c4e9b2c3720 100644 --- a/vcl/inc/vcl/outdev.hxx +++ b/vcl/inc/vcl/outdev.hxx @@ -41,7 +41,7 @@ #include <com/sun/star/uno/Reference.h> #include <unotools/fontdefs.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> - +#include <com/sun/star/drawing/LineCap.hpp> #include <vector> struct ImplOutDevData; @@ -556,7 +556,11 @@ public: // #i101491# // Helper who tries to use SalGDI's DrawPolyLine direct and returns it's bool. Contains no AA check. - SAL_DLLPRIVATE bool ImpTryDrawPolyLineDirect(const basegfx::B2DPolygon& rB2DPolygon, double fLineWidth, basegfx::B2DLineJoin eLineJoin); + SAL_DLLPRIVATE bool ImpTryDrawPolyLineDirect( + const basegfx::B2DPolygon& rB2DPolygon, + double fLineWidth = 0.0, + basegfx::B2DLineJoin eLineJoin = basegfx::B2DLINEJOIN_NONE, + com::sun::star::drawing::LineCap eLineCap = com::sun::star::drawing::LineCap_BUTT); // Helper for line geometry paint with support for graphic expansion (pattern and fat_to_area) void impPaintLineGeometryWithEvtlExpand(const LineInfo& rInfo, basegfx::B2DPolyPolygon aLinePolyPolygon); @@ -687,7 +691,11 @@ public: @see DrawPolyPolygon */ void DrawPolyLine( const Polygon& rPoly ); - void DrawPolyLine( const basegfx::B2DPolygon&, double fLineWidth = 0.0, basegfx::B2DLineJoin = basegfx::B2DLINEJOIN_ROUND ); + void DrawPolyLine( + const basegfx::B2DPolygon&, + double fLineWidth = 0.0, + basegfx::B2DLineJoin = basegfx::B2DLINEJOIN_ROUND, + com::sun::star::drawing::LineCap = com::sun::star::drawing::LineCap_BUTT); /** Render the given polygon as a line stroke diff --git a/vcl/inc/vcl/print.hxx b/vcl/inc/vcl/print.hxx index 755197147563..de0649a3b4ea 100644 --- a/vcl/inc/vcl/print.hxx +++ b/vcl/inc/vcl/print.hxx @@ -84,11 +84,6 @@ public: GDIMetaFile* GetGDIMetaFile() const { return mpMtf; } const JobSetup& GetJobSetup() const { return maJobSetup; } sal_Bool IsNewJobSetup() const { return (mbNewJobSetup != 0); } - - friend VCL_DLLPUBLIC SvStream& operator<<( SvStream& rOStm, const PrinterPage& rPage ) - { rOStm << rPage.mbNewJobSetup; rOStm << rPage.maJobSetup; rPage.mpMtf->Write( rOStm ); return rOStm; } - friend VCL_DLLPUBLIC SvStream& operator>>( SvStream& rIStm, PrinterPage& rPage ) - { return rIStm >> rPage.mbNewJobSetup >> rPage.maJobSetup >> *rPage.mpMtf; } }; diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h index 9227b2da7005..1ba1c51f36fb 100755 --- a/vcl/inc/win/salgdi.h +++ b/vcl/inc/win/salgdi.h @@ -186,7 +186,12 @@ protected: virtual void drawPolygon( sal_uIntPtr nPoints, const SalPoint* pPtAry ); virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ); virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidth, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap); virtual sal_Bool drawPolyLineBezier( sal_uIntPtr nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); virtual sal_Bool drawPolygonBezier( sal_uIntPtr nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ); virtual sal_Bool drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const BYTE* const* pFlgAry ); diff --git a/vcl/source/gdi/cvtsvm.cxx b/vcl/source/gdi/cvtsvm.cxx index 10e9971e08b1..414b0c51600f 100644 --- a/vcl/source/gdi/cvtsvm.cxx +++ b/vcl/source/gdi/cvtsvm.cxx @@ -599,6 +599,14 @@ void SVMConverter::ImplConvertFromSVM1( SvStream& rIStm, GDIMetaFile& rMtf ) } break; + case (GDI_LINECAP_ACTION) : + { + sal_Int16 nLineCap(0); + rIStm >> nLineCap; + aLineInfo.SetLineCap((com::sun::star::drawing::LineCap)nLineCap); + } + break; + case (GDI_LINEDASHDOT_ACTION) : { sal_Int16 a(0); @@ -1457,6 +1465,7 @@ sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, const LineInfo& rInfo = pAct->GetLineInfo(); const bool bFatLine(!rInfo.IsDefault() && (LINE_NONE != rInfo.GetStyle())); const bool bLineJoin(bFatLine && basegfx::B2DLINEJOIN_ROUND != rInfo.GetLineJoin()); + const bool bLineCap(bFatLine && com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap()); const bool bLineDashDot(LINE_DASH == rInfo.GetStyle()); if( bFatLine ) @@ -1471,18 +1480,25 @@ sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, rOStm << (sal_Int16) rInfo.GetLineJoin(); } - if(bLineDashDot) + if(bLineCap) { - rOStm << (sal_Int16) GDI_LINEDASHDOT_ACTION; - rOStm << (sal_Int32) 4 + 16; - rOStm << (sal_Int16)rInfo.GetDashCount(); - rOStm << (sal_Int32)rInfo.GetDashLen(); - rOStm << (sal_Int16)rInfo.GetDotCount(); - rOStm << (sal_Int32)rInfo.GetDotLen(); - rOStm << (sal_Int32)rInfo.GetDistance(); + rOStm << (sal_Int16) GDI_LINECAP_ACTION; + rOStm << (sal_Int32) 6; + rOStm << (sal_Int16) rInfo.GetLineCap(); } } + if(bLineDashDot) + { + rOStm << (sal_Int16) GDI_LINEDASHDOT_ACTION; + rOStm << (sal_Int32) 4 + 16; + rOStm << (sal_Int16)rInfo.GetDashCount(); + rOStm << (sal_Int32)rInfo.GetDashLen(); + rOStm << (sal_Int16)rInfo.GetDotCount(); + rOStm << (sal_Int32)rInfo.GetDotLen(); + rOStm << (sal_Int32)rInfo.GetDistance(); + } + rOStm << (sal_Int16) GDI_LINE_ACTION; rOStm << (sal_Int32) 20; rOStm << pAct->GetStartPoint(); @@ -1499,11 +1515,16 @@ sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, nCount += 1; } - if(bLineDashDot) + if(bLineCap) { nCount += 1; } } + + if(bLineDashDot) + { + nCount += 1; + } } break; @@ -1600,6 +1621,7 @@ sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, const sal_uInt16 nPoints(aSimplePoly.GetSize()); const bool bFatLine(!rInfo.IsDefault() && (LINE_NONE != rInfo.GetStyle())); const bool bLineJoin(bFatLine && basegfx::B2DLINEJOIN_ROUND != rInfo.GetLineJoin()); + const bool bLineCap(bFatLine && com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap()); const bool bLineDashDot(LINE_DASH == rInfo.GetStyle()); if( bFatLine ) @@ -1613,6 +1635,13 @@ sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, rOStm << (sal_Int32) 6; rOStm << (sal_Int16) rInfo.GetLineJoin(); } + + if(bLineCap) + { + rOStm << (sal_Int16) GDI_LINECAP_ACTION; + rOStm << (sal_Int32) 6; + rOStm << (sal_Int16) rInfo.GetLineCap(); + } } if(bLineDashDot) @@ -1652,6 +1681,11 @@ sal_uLong SVMConverter::ImplWriteActions( SvStream& rOStm, GDIMetaFile& rMtf, { nCount += 1; } + + if(bLineCap) + { + nCount += 1; + } } if(bLineDashDot) diff --git a/vcl/source/gdi/lineinfo.cxx b/vcl/source/gdi/lineinfo.cxx index 339b17792825..87c313c2e48c 100644 --- a/vcl/source/gdi/lineinfo.cxx +++ b/vcl/source/gdi/lineinfo.cxx @@ -47,7 +47,8 @@ ImplLineInfo::ImplLineInfo() : mnDotCount ( 0 ), mnDotLen ( 0 ), mnDistance ( 0 ), - meLineJoin ( basegfx::B2DLINEJOIN_ROUND ) + meLineJoin ( basegfx::B2DLINEJOIN_ROUND ), + meLineCap ( com::sun::star::drawing::LineCap_BUTT ) { } @@ -62,7 +63,8 @@ ImplLineInfo::ImplLineInfo( const ImplLineInfo& rImplLineInfo ) : mnDotCount ( rImplLineInfo.mnDotCount ), mnDotLen ( rImplLineInfo.mnDotLen ), mnDistance ( rImplLineInfo.mnDistance ), - meLineJoin ( rImplLineInfo.meLineJoin ) + meLineJoin ( rImplLineInfo.meLineJoin ), + meLineCap ( rImplLineInfo.meLineCap ) { } @@ -77,7 +79,8 @@ inline bool ImplLineInfo::operator==( const ImplLineInfo& rB ) const && mnDotCount == rB.mnDotCount && mnDotLen == rB.mnDotLen && mnDistance == rB.mnDistance - && meLineJoin == rB.meLineJoin); + && meLineJoin == rB.meLineJoin + && meLineCap == rB.meLineCap); } // ------------ @@ -229,6 +232,28 @@ void LineInfo::SetLineJoin(basegfx::B2DLineJoin eLineJoin) // ----------------------------------------------------------------------- +void LineInfo::SetLineCap(com::sun::star::drawing::LineCap eLineCap) +{ + DBG_CHKTHIS( LineInfo, NULL ); + + if(eLineCap != mpImplLineInfo->meLineCap) + { + ImplMakeUnique(); + mpImplLineInfo->meLineCap = eLineCap; + } +} + +// ----------------------------------------------------------------------- + +sal_Bool LineInfo::IsDefault() const +{ + return( !mpImplLineInfo->mnWidth + && ( LINE_SOLID == mpImplLineInfo->meStyle ) + && ( com::sun::star::drawing::LineCap_BUTT == mpImplLineInfo->meLineCap)); +} + +// ----------------------------------------------------------------------- + SvStream& operator>>( SvStream& rIStm, ImplLineInfo& rImplLineInfo ) { VersionCompat aCompat( rIStm, STREAM_READ ); @@ -251,6 +276,12 @@ SvStream& operator>>( SvStream& rIStm, ImplLineInfo& rImplLineInfo ) rIStm >> nTmp16; rImplLineInfo.meLineJoin = (basegfx::B2DLineJoin) nTmp16; } + if( aCompat.GetVersion() >= 4 ) + { + // version 4 + rIStm >> nTmp16; rImplLineInfo.meLineCap = (com::sun::star::drawing::LineCap) nTmp16; + } + return rIStm; } @@ -258,7 +289,7 @@ SvStream& operator>>( SvStream& rIStm, ImplLineInfo& rImplLineInfo ) SvStream& operator<<( SvStream& rOStm, const ImplLineInfo& rImplLineInfo ) { - VersionCompat aCompat( rOStm, STREAM_WRITE, 3 ); + VersionCompat aCompat( rOStm, STREAM_WRITE, 4 ); // version 1 rOStm << (sal_uInt16) rImplLineInfo.meStyle << rImplLineInfo.mnWidth; @@ -271,6 +302,9 @@ SvStream& operator<<( SvStream& rOStm, const ImplLineInfo& rImplLineInfo ) // since version3 rOStm << (sal_uInt16) rImplLineInfo.meLineJoin; + // since version4 + rOStm << (sal_uInt16) rImplLineInfo.meLineCap; + return rOStm; } @@ -354,7 +388,8 @@ void LineInfo::applyToB2DPolyPolygon( o_rFillPolyPolygon.append(basegfx::tools::createAreaGeometry( io_rLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, - GetLineJoin())); + GetLineJoin(), + GetLineCap())); } io_rLinePolyPolygon.clear(); diff --git a/vcl/source/gdi/outdev.cxx b/vcl/source/gdi/outdev.cxx index 6a57320c763c..48f4195cbb54 100644 --- a/vcl/source/gdi/outdev.cxx +++ b/vcl/source/gdi/outdev.cxx @@ -1509,7 +1509,7 @@ void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt ) aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine); } - if( mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this)) + if( mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, com::sun::star::drawing::LineCap_BUTT, this)) { return; } @@ -1596,7 +1596,8 @@ void OutputDevice::impPaintLineGeometryWithEvtlExpand( aFillPolyPolygon.append(basegfx::tools::createAreaGeometry( aLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, - rInfo.GetLineJoin())); + rInfo.GetLineJoin(), + rInfo.GetLineCap())); } aLinePolyPolygon.clear(); @@ -1614,7 +1615,7 @@ void OutputDevice::impPaintLineGeometryWithEvtlExpand( if(bTryAA) { - bDone = mpGraphics->DrawPolyLine( aCandidate, 0.0, basegfx::B2DVector(1.0,1.0), basegfx::B2DLINEJOIN_NONE, this); + bDone = mpGraphics->DrawPolyLine( aCandidate, 0.0, basegfx::B2DVector(1.0,1.0), basegfx::B2DLINEJOIN_NONE, com::sun::star::drawing::LineCap_BUTT, this); } if(!bDone) @@ -1646,7 +1647,10 @@ void OutputDevice::impPaintLineGeometryWithEvtlExpand( { for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++) { - const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a)); + Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a)); + + // need to subdivide, mpGraphics->DrawPolygon ignores curves + aPolygon.AdaptiveSubdivide(aPolygon); mpGraphics->DrawPolygon(aPolygon.GetSize(), (const SalPoint*)aPolygon.GetConstPointAry(), this); } } @@ -1789,7 +1793,7 @@ void OutputDevice::DrawPolyLine( const Polygon& rPoly ) && IsLineColor()); // use b2dpolygon drawing if possible - if(bTryAA && ImpTryDrawPolyLineDirect(rPoly.getB2DPolygon(), 0.0, basegfx::B2DLINEJOIN_NONE)) + if(bTryAA && ImpTryDrawPolyLineDirect(rPoly.getB2DPolygon())) { basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon()); const ::basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); @@ -1803,7 +1807,7 @@ void OutputDevice::DrawPolyLine( const Polygon& rPoly ) aB2DPolyLine = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolyLine); } - if(mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this)) + if(mpGraphics->DrawPolyLine( aB2DPolyLine, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, com::sun::star::drawing::LineCap_BUTT, this)) { return; } @@ -1851,7 +1855,7 @@ void OutputDevice::DrawPolyLine( const Polygon& rPoly, const LineInfo& rLineInfo if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW) && LINE_SOLID == rLineInfo.GetStyle()) { - DrawPolyLine( rPoly.getB2DPolygon(), (double)rLineInfo.GetWidth(), rLineInfo.GetLineJoin()); + DrawPolyLine( rPoly.getB2DPolygon(), (double)rLineInfo.GetWidth(), rLineInfo.GetLineJoin(), rLineInfo.GetLineCap()); return; } @@ -1983,7 +1987,13 @@ void OutputDevice::DrawPolygon( const Polygon& rPoly ) aB2DPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aB2DPolygon); } - bSuccess = mpGraphics->DrawPolyLine( aB2DPolygon, 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this); + bSuccess = mpGraphics->DrawPolyLine( + aB2DPolygon, + 0.0, + aB2DLineWidth, + basegfx::B2DLINEJOIN_NONE, + com::sun::star::drawing::LineCap_BUTT, + this); } if(bSuccess) @@ -2075,7 +2085,13 @@ void OutputDevice::DrawPolyPolygon( const PolyPolygon& rPolyPoly ) for(sal_uInt32 a(0); bSuccess && a < aB2DPolyPolygon.count(); a++) { - bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a), 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this); + bSuccess = mpGraphics->DrawPolyLine( + aB2DPolyPolygon.getB2DPolygon(a), + 0.0, + aB2DLineWidth, + basegfx::B2DLINEJOIN_NONE, + com::sun::star::drawing::LineCap_BUTT, + this); } } @@ -2198,7 +2214,13 @@ void OutputDevice::ImpDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPo for(sal_uInt32 a(0);bSuccess && a < aB2DPolyPolygon.count(); a++) { - bSuccess = mpGraphics->DrawPolyLine( aB2DPolyPolygon.getB2DPolygon(a), 0.0, aB2DLineWidth, basegfx::B2DLINEJOIN_NONE, this); + bSuccess = mpGraphics->DrawPolyLine( + aB2DPolyPolygon.getB2DPolygon(a), + 0.0, + aB2DLineWidth, + basegfx::B2DLINEJOIN_NONE, + com::sun::star::drawing::LineCap_BUTT, + this); } } @@ -2219,7 +2241,8 @@ void OutputDevice::ImpDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPo bool OutputDevice::ImpTryDrawPolyLineDirect( const basegfx::B2DPolygon& rB2DPolygon, double fLineWidth, - basegfx::B2DLineJoin eLineJoin) + basegfx::B2DLineJoin eLineJoin, + com::sun::star::drawing::LineCap eLineCap) { const basegfx::B2DHomMatrix aTransform = ImplGetDeviceTransformation(); basegfx::B2DVector aB2DLineWidth(1.0, 1.0); @@ -2245,17 +2268,25 @@ bool OutputDevice::ImpTryDrawPolyLineDirect( } // draw the polyline - return mpGraphics->DrawPolyLine( aB2DPolygon, 0.0, aB2DLineWidth, eLineJoin, this); + return mpGraphics->DrawPolyLine( + aB2DPolygon, + 0.0, + aB2DLineWidth, + eLineJoin, + eLineCap, + this); } void OutputDevice::DrawPolyLine( const basegfx::B2DPolygon& rB2DPolygon, double fLineWidth, - basegfx::B2DLineJoin eLineJoin) + basegfx::B2DLineJoin eLineJoin, + com::sun::star::drawing::LineCap eLineCap) { DBG_TRACE( "OutputDevice::DrawPolyLine(B2D&)" ); DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); (void)eLineJoin; // ATM used in UNX, but not in WNT, access it for warning-free + (void)eLineCap; #if 0 // MetaB2DPolyLineAction is not implemented yet: // according to AW adding it is very dangerous since there is a lot @@ -2297,7 +2328,7 @@ void OutputDevice::DrawPolyLine( && IsLineColor()); // use b2dpolygon drawing if possible - if(bTryAA && ImpTryDrawPolyLineDirect(rB2DPolygon, fLineWidth, eLineJoin)) + if(bTryAA && ImpTryDrawPolyLineDirect(rB2DPolygon, fLineWidth, eLineJoin, eLineCap)) { return; } @@ -2311,9 +2342,12 @@ void OutputDevice::DrawPolyLine( && rB2DPolygon.count() <= 1000) { const double fHalfLineWidth((fLineWidth * 0.5) + 0.5); - const basegfx::B2DPolyPolygon aAreaPolyPolygon(basegfx::tools::createAreaGeometry( - rB2DPolygon, fHalfLineWidth, eLineJoin)); - + const basegfx::B2DPolyPolygon aAreaPolyPolygon( + basegfx::tools::createAreaGeometry( + rB2DPolygon, + fHalfLineWidth, + eLineJoin, + eLineCap)); const Color aOldLineColor(maLineColor); const Color aOldFillColor(maFillColor); @@ -2340,7 +2374,7 @@ void OutputDevice::DrawPolyLine( // to avoid optical gaps for(sal_uInt32 a(0); a < aAreaPolyPolygon.count(); a++) { - ImpTryDrawPolyLineDirect(aAreaPolyPolygon.getB2DPolygon(a), 0.0, basegfx::B2DLINEJOIN_NONE); + ImpTryDrawPolyLineDirect(aAreaPolyPolygon.getB2DPolygon(a)); } } } diff --git a/vcl/source/gdi/outdev6.cxx b/vcl/source/gdi/outdev6.cxx index f4b4dee908c6..626c0989db1e 100644 --- a/vcl/source/gdi/outdev6.cxx +++ b/vcl/source/gdi/outdev6.cxx @@ -200,7 +200,7 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) { const ::basegfx::B2DPolygon aOnePoly = aB2DPolyPolygon.getB2DPolygon( nPolyIdx ); - mpGraphics->DrawPolyLine( aOnePoly, fTransparency, aHairlineWidth, ::basegfx::B2DLINEJOIN_NONE, this ); + mpGraphics->DrawPolyLine( aOnePoly, fTransparency, aHairlineWidth, ::basegfx::B2DLINEJOIN_NONE, com::sun::star::drawing::LineCap_BUTT, this ); } } @@ -317,7 +317,7 @@ void OutputDevice::DrawTransparent( const PolyPolygon& rPolyPoly, for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) { const ::basegfx::B2DPolygon& rPolygon = aB2DPolyPolygon.getB2DPolygon( nPolyIdx ); - bDrawn = mpGraphics->DrawPolyLine( rPolygon, fTransparency, aLineWidths, ::basegfx::B2DLINEJOIN_NONE, this ); + bDrawn = mpGraphics->DrawPolyLine( rPolygon, fTransparency, aLineWidths, ::basegfx::B2DLINEJOIN_NONE, com::sun::star::drawing::LineCap_BUTT, this ); } // prepare to restore the fill color mbInitFillColor = mbFillColor; diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 896d322185cb..8655cf126588 100755 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -1624,7 +1624,28 @@ void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const { - bool bRet = true; + if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen()) + { + // dashed and non-degraded case, check for implementation limits of dash array + // in PDF reader apps (e.g. acroread) + if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10) + { + return false; + } + } + + if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin()) + { + // LineJoin used, ExtLineInfo required + return false; + } + + if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap()) + { + // LineCap used, ExtLineInfo required + return false; + } + if( rInfo.GetStyle() == LINE_DASH ) { rBuffer.append( "[ " ); @@ -1637,10 +1658,6 @@ bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffe } else { - // check for implementation limits of dash array - // in PDF reader apps (e.g. acroread) - if( 2*(rInfo.GetDashCount() + rInfo.GetDotCount()) > 10 ) - bRet = false; for( int n = 0; n < rInfo.GetDashCount(); n++ ) { appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); @@ -1658,6 +1675,7 @@ bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffe } rBuffer.append( "] 0 d\n" ); } + if( rInfo.GetWidth() > 1 ) { appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer ); @@ -1669,7 +1687,8 @@ bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffe appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer ); rBuffer.append( " w\n" ); } - return bRet; + + return true; } void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const @@ -9045,21 +9064,68 @@ void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter rOut.m_fMiterLimit = 10; rOut.m_aDashArray.clear(); - int nDashes = rIn.GetDashCount(); - int nDashLen = rIn.GetDashLen(); - int nDistance = rIn.GetDistance(); + // add DashDot to DashArray + const int nDashes = rIn.GetDashCount(); + const int nDashLen = rIn.GetDashLen(); + const int nDistance = rIn.GetDistance(); + for( int n = 0; n < nDashes; n++ ) { rOut.m_aDashArray.push_back( nDashLen ); rOut.m_aDashArray.push_back( nDistance ); } - int nDots = rIn.GetDotCount(); - int nDotLen = rIn.GetDotLen(); + + const int nDots = rIn.GetDotCount(); + const int nDotLen = rIn.GetDotLen(); + for( int n = 0; n < nDots; n++ ) { rOut.m_aDashArray.push_back( nDotLen ); rOut.m_aDashArray.push_back( nDistance ); } + + // add LineJoin + switch(rIn.GetLineJoin()) + { + case basegfx::B2DLINEJOIN_BEVEL : + { + rOut.m_eJoin = PDFWriter::joinBevel; + break; + } + default : // basegfx::B2DLINEJOIN_NONE : + // Pdf has no 'none' lineJoin, default is miter + case basegfx::B2DLINEJOIN_MIDDLE : + case basegfx::B2DLINEJOIN_MITER : + { + rOut.m_eJoin = PDFWriter::joinMiter; + break; + } + case basegfx::B2DLINEJOIN_ROUND : + { + rOut.m_eJoin = PDFWriter::joinRound; + break; + } + } + + // add LineCap + switch(rIn.GetLineCap()) + { + default: /* com::sun::star::drawing::LineCap_BUTT */ + { + rOut.m_eCap = PDFWriter::capButt; + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + rOut.m_eCap = PDFWriter::capRound; + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + rOut.m_eCap = PDFWriter::capSquare; + break; + } + } } void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx index 094acf76cb92..416d97558c32 100755 --- a/vcl/source/gdi/salgdilayout.cxx +++ b/vcl/source/gdi/salgdilayout.cxx @@ -412,7 +412,8 @@ bool SalGraphics::drawPolyLine( const basegfx::B2DPolygon& /*rPolyPolygon*/, double /*fTransparency*/, const basegfx::B2DVector& /*rLineWidths*/, - basegfx::B2DLineJoin /*eLineJoin*/) + basegfx::B2DLineJoin /*eLineJoin*/, + com::sun::star::drawing::LineCap /*eLineCap*/) { return false; } @@ -542,18 +543,22 @@ sal_Bool SalGraphics::DrawPolyPolygonBezier( sal_uInt32 i_nPoly, const sal_uInt3 return bRet; } -bool SalGraphics::DrawPolyLine( const ::basegfx::B2DPolygon& i_rPolygon, double fTransparency, - const ::basegfx::B2DVector& i_rLineWidth, basegfx::B2DLineJoin i_eLineJoin, +bool SalGraphics::DrawPolyLine( + const ::basegfx::B2DPolygon& i_rPolygon, + double i_fTransparency, + const ::basegfx::B2DVector& i_rLineWidth, + basegfx::B2DLineJoin i_eLineJoin, + com::sun::star::drawing::LineCap i_eLineCap, const OutputDevice* i_pOutDev ) { bool bRet = false; if( (m_nLayout & SAL_LAYOUT_BIDI_RTL) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) ) { basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) ); - bRet = drawPolyLine( aMirror, fTransparency, i_rLineWidth, i_eLineJoin ); + bRet = drawPolyLine( aMirror, i_fTransparency, i_rLineWidth, i_eLineJoin, i_eLineCap ); } else - bRet = drawPolyLine( i_rPolygon, fTransparency, i_rLineWidth, i_eLineJoin ); + bRet = drawPolyLine( i_rPolygon, i_fTransparency, i_rLineWidth, i_eLineJoin, i_eLineCap ); return bRet; } diff --git a/vcl/unx/generic/gdi/pspgraphics.cxx b/vcl/unx/generic/gdi/pspgraphics.cxx index d2a1942f419a..188cff6be9dd 100644 --- a/vcl/unx/generic/gdi/pspgraphics.cxx +++ b/vcl/unx/generic/gdi/pspgraphics.cxx @@ -401,7 +401,12 @@ bool PspGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double /*fT return false; } -bool PspGraphics::drawPolyLine( const basegfx::B2DPolygon&, double /*fTranspareny*/, const basegfx::B2DVector& /*rLineWidths*/, basegfx::B2DLineJoin /*eJoin*/) +bool PspGraphics::drawPolyLine( + const basegfx::B2DPolygon&, + double /*fTranspareny*/, + const basegfx::B2DVector& /*rLineWidths*/, + basegfx::B2DLineJoin /*eJoin*/, + com::sun::star::drawing::LineCap /*eLineCap*/) { // TODO: a PS printer can draw B2DPolyLines almost directly return false; diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx index 79b2a1b1319f..d7a3eccb0d09 100644 --- a/vcl/unx/generic/gdi/salgdi.cxx +++ b/vcl/unx/generic/gdi/salgdi.cxx @@ -1186,7 +1186,12 @@ bool X11SalGraphics::drawFilledTrapezoids( const ::basegfx::B2DTrapezoid* pB2DTr // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -bool X11SalGraphics::drawPolyLine(const ::basegfx::B2DPolygon& rPolygon, double fTransparency, const ::basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin) +bool X11SalGraphics::drawPolyLine( + const ::basegfx::B2DPolygon& rPolygon, + double fTransparency, + const ::basegfx::B2DVector& rLineWidth, + basegfx::B2DLineJoin eLineJoin, + com::sun::star::drawing::LineCap eLineCap) { const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2); @@ -1240,7 +1245,7 @@ bool X11SalGraphics::drawPolyLine(const ::basegfx::B2DPolygon& rPolygon, double } // create the area-polygon for the line - const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::tools::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin) ); + const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::tools::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin, eLineCap) ); if( (rLineWidth.getX() != rLineWidth.getY()) && !basegfx::fTools::equalZero( rLineWidth.getX() ) ) diff --git a/vcl/unx/headless/svpgdi.cxx b/vcl/unx/headless/svpgdi.cxx index 45cb3214ab7e..c3f4ce549e1e 100644 --- a/vcl/unx/headless/svpgdi.cxx +++ b/vcl/unx/headless/svpgdi.cxx @@ -372,7 +372,12 @@ void SvpSalGraphics::drawPolyPolygon( sal_uInt32 nPoly, dbgOut( m_aDevice ); } -bool SvpSalGraphics::drawPolyLine( const ::basegfx::B2DPolygon&, double /*fTransparency*/, const ::basegfx::B2DVector& /*rLineWidths*/, basegfx::B2DLineJoin /*eJoin*/ ) +bool SvpSalGraphics::drawPolyLine( + const ::basegfx::B2DPolygon&, + double /*fTransparency*/, + const ::basegfx::B2DVector& /*rLineWidths*/, + basegfx::B2DLineJoin /*eJoin*/, + com::sun::star::drawing::LineCap /*eLineCap*/) { // TODO: implement and advertise OutDevSupport_B2DDraw support return false; diff --git a/vcl/unx/headless/svpgdi.hxx b/vcl/unx/headless/svpgdi.hxx index 93d821ea228a..f36e72aa4c19 100644 --- a/vcl/unx/headless/svpgdi.hxx +++ b/vcl/unx/headless/svpgdi.hxx @@ -115,7 +115,12 @@ public: virtual void drawLine( long nX1, long nY1, long nX2, long nY2 ); virtual void drawRect( long nX, long nY, long nWidth, long nHeight ); virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap); virtual void drawPolyLine( sal_uLong nPoints, const SalPoint* pPtAry ); virtual void drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry ); virtual void drawPolyPolygon( sal_uInt32 nPoly, diff --git a/vcl/unx/headless/svppspgraphics.cxx b/vcl/unx/headless/svppspgraphics.cxx index 93c22878f775..281f8c57b53a 100644 --- a/vcl/unx/headless/svppspgraphics.cxx +++ b/vcl/unx/headless/svppspgraphics.cxx @@ -320,7 +320,12 @@ void PspGraphics::drawPolyPolygon( sal_uInt32 nPoly, m_pPrinterGfx->DrawPolyPolygon (nPoly, pPoints, (const Point**)pPtAry); } -bool PspGraphics::drawPolyLine( const ::basegfx::B2DPolygon&, double /*fTransparency*/, const ::basegfx::B2DVector& /*rLineWidths*/, basegfx::B2DLineJoin /*eJoin*/ ) +bool PspGraphics::drawPolyLine( + const ::basegfx::B2DPolygon&, + double /*fTransparency*/, + const ::basegfx::B2DVector& /*rLineWidths*/, + basegfx::B2DLineJoin /*eJoin*/, + com::sun::star::drawing::LineCap /*eLineCap*/) { // TODO: implement and advertise OutDevSupport_B2DDraw support return false; diff --git a/vcl/unx/headless/svppspgraphics.hxx b/vcl/unx/headless/svppspgraphics.hxx index ba2ff841eee6..f8a4d182f369 100644 --- a/vcl/unx/headless/svppspgraphics.hxx +++ b/vcl/unx/headless/svppspgraphics.hxx @@ -136,7 +136,12 @@ public: virtual void drawPolyLine( sal_uLong nPoints, const SalPoint* pPtAry ); virtual void drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry ); virtual bool drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double fTransparency ); - virtual bool drawPolyLine( const ::basegfx::B2DPolygon&, double fTransparency, const ::basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin ); + virtual bool drawPolyLine( + const ::basegfx::B2DPolygon&, + double fTransparency, + const ::basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin, + com::sun::star::drawing::LineCap); virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ); diff --git a/vcl/win/source/gdi/salgdi_gdiplus.cxx b/vcl/win/source/gdi/salgdi_gdiplus.cxx index f8430d25bc5d..6fc73b86d02c 100644 --- a/vcl/win/source/gdi/salgdi_gdiplus.cxx +++ b/vcl/win/source/gdi/salgdi_gdiplus.cxx @@ -187,7 +187,12 @@ bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly return true; } -bool WinSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolygon, double fTransparency, const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin eLineJoin ) +bool WinSalGraphics::drawPolyLine( + const basegfx::B2DPolygon& rPolygon, + double fTransparency, + const basegfx::B2DVector& rLineWidths, + basegfx::B2DLineJoin eLineJoin, + com::sun::star::drawing::LineCap eLineCap) { const sal_uInt32 nCount(rPolygon.count()); @@ -230,6 +235,27 @@ bool WinSalGraphics::drawPolyLine( const basegfx::B2DPolygon& rPolygon, double f } } + switch(eLineCap) + { + default: /*com::sun::star::drawing::LineCap_BUTT*/ + { + // nothing to do + break; + } + case com::sun::star::drawing::LineCap_ROUND: + { + aTestPen.SetStartCap(Gdiplus::LineCapRound); + aTestPen.SetEndCap(Gdiplus::LineCapRound); + break; + } + case com::sun::star::drawing::LineCap_SQUARE: + { + aTestPen.SetStartCap(Gdiplus::LineCapSquare); + aTestPen.SetEndCap(Gdiplus::LineCapSquare); + break; + } + } + if(nCount > 250 && basegfx::fTools::more(rLineWidths.getX(), 1.5)) { impAddB2DPolygonToGDIPlusGraphicsPathInteger(aPath, rPolygon, bNoLineJoin); diff --git a/xmloff/inc/xmloff/xmltoken.hxx b/xmloff/inc/xmloff/xmltoken.hxx index d13b6cef4cd0..e8e1b8a27ff9 100644 --- a/xmloff/inc/xmloff/xmltoken.hxx +++ b/xmloff/inc/xmloff/xmltoken.hxx @@ -340,6 +340,7 @@ namespace xmloff { namespace token { XML_BUBBLE, XML_BULLET_CHAR, XML_BULLET_RELATIVE_SIZE, + XML_BUTT, XML_BUTTON1, XML_BUTTON2, XML_BUTTON3, @@ -1681,6 +1682,7 @@ namespace xmloff { namespace token { XML_STROKE, XML_STROKE_COLOR, XML_STROKE_DASH, + XML_STROKE_LINECAP, XML_STROKE_LINEJOIN, XML_STROKE_OPACITY, XML_STROKE_WIDTH, diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index bdf0649cfad2..17f62003397a 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -348,6 +348,7 @@ namespace xmloff { namespace token { TOKEN( "bubble", XML_BUBBLE ), TOKEN( "bullet-char", XML_BULLET_CHAR ), TOKEN( "bullet-relative-size", XML_BULLET_RELATIVE_SIZE ), + TOKEN( "butt", XML_BUTT ), TOKEN( "button1", XML_BUTTON1 ), TOKEN( "button2", XML_BUTTON2 ), TOKEN( "button3", XML_BUTTON3 ), @@ -1689,6 +1690,7 @@ namespace xmloff { namespace token { TOKEN( "stroke", XML_STROKE ), TOKEN( "stroke-color", XML_STROKE_COLOR ), TOKEN( "stroke-dash", XML_STROKE_DASH ), + TOKEN( "stroke-linecap", XML_STROKE_LINECAP ), TOKEN( "stroke-linejoin", XML_STROKE_LINEJOIN ), TOKEN( "stroke-opacity", XML_STROKE_OPACITY ), TOKEN( "stroke-width", XML_STROKE_WIDTH ), diff --git a/xmloff/source/draw/sdpropls.cxx b/xmloff/source/draw/sdpropls.cxx index 415d8ccbefc5..79863c575b5b 100644 --- a/xmloff/source/draw/sdpropls.cxx +++ b/xmloff/source/draw/sdpropls.cxx @@ -28,6 +28,7 @@ #include <com/sun/star/container/XIndexReplace.hpp> #include <com/sun/star/drawing/LineStyle.hpp> #include <com/sun/star/drawing/LineJoint.hpp> +#include <com/sun/star/drawing/LineCap.hpp> #include <com/sun/star/drawing/FillStyle.hpp> #include <com/sun/star/presentation/AnimationSpeed.hpp> #include <com/sun/star/presentation/FadeEffect.hpp> @@ -111,6 +112,7 @@ const XMLPropertyMapEntry aXMLSDProperties[] = GMAP( "LineEndCenter", XML_NAMESPACE_DRAW, XML_MARKER_END_CENTER, XML_TYPE_BOOL, 0 ), GMAP( "LineTransparence", XML_NAMESPACE_SVG, XML_STROKE_OPACITY, XML_SD_TYPE_OPACITY, 0 ), GMAP( "LineJoint", XML_NAMESPACE_DRAW, XML_STROKE_LINEJOIN, XML_SD_TYPE_LINEJOIN, 0 ), + GMAP( "LineCap", XML_NAMESPACE_SVG , XML_STROKE_LINECAP, XML_SD_TYPE_LINECAP, 0 ), // fill attributes GMAP( "FillStyle", XML_NAMESPACE_DRAW, XML_FILL, XML_SD_TYPE_FILLSTYLE, 0 ), @@ -392,6 +394,14 @@ SvXMLEnumMapEntry aXML_LineJoint_EnumMap[] = { XML_TOKEN_INVALID, 0 } }; +SvXMLEnumMapEntry aXML_LineCap_EnumMap[] = +{ + { XML_BUTT, drawing::LineCap_BUTT }, + { XML_ROUND, drawing::LineCap_ROUND }, + { XML_GRADIENTSTYLE_SQUARE, drawing::LineCap_SQUARE }, // use XML_GRADIENTSTYLE_SQUARE as XML_SQUARE, is defined as "square" already + { XML_TOKEN_INVALID, 0 } +}; + SvXMLEnumMapEntry aXML_FillStyle_EnumMap[] = { { XML_NONE, drawing::FillStyle_NONE }, @@ -887,6 +897,11 @@ const XMLPropertyHandler* XMLSdPropHdlFactory::GetPropertyHandler( sal_Int32 nTy pHdl = new XMLEnumPropertyHdl( aXML_LineJoint_EnumMap, ::getCppuType((const drawing::LineJoint*)0) ); break; } + case XML_SD_TYPE_LINECAP : + { + pHdl = new XMLEnumPropertyHdl( aXML_LineCap_EnumMap, ::getCppuType((const drawing::LineCap*)0) ); + break; + } case XML_SD_TYPE_FILLSTYLE : { pHdl = new XMLEnumPropertyHdl( aXML_FillStyle_EnumMap, ::getCppuType((const drawing::FillStyle*)0) ); diff --git a/xmloff/source/draw/sdpropls.hxx b/xmloff/source/draw/sdpropls.hxx index 86113cdc4ac0..b5befc3d1161 100644 --- a/xmloff/source/draw/sdpropls.hxx +++ b/xmloff/source/draw/sdpropls.hxx @@ -83,6 +83,7 @@ extern const XMLPropertyMapEntry aXMLSDPresPageProps_onlyHeadersFooter[]; #define XML_SD_TYPE_CONTROL_BORDER (XML_SD_TYPES_START + 32 ) #define XML_SD_TYPE_CONTROL_BORDER_COLOR (XML_SD_TYPES_START + 33 ) #define XML_SD_TYPE_IMAGE_SCALE_MODE (XML_SD_TYPES_START + 34 ) +#define XML_SD_TYPE_LINECAP (XML_SD_TYPES_START + 35 ) // 3D property types #define XML_SD_TYPE_BACKFACE_CULLING (XML_SD_TYPES_START + 40 ) |