summaryrefslogtreecommitdiff
path: root/svgio
diff options
context:
space:
mode:
authorArmin Le Grand <alg@apache.org>2013-10-11 16:57:46 +0000
committerCaolán McNamara <caolanm@redhat.com>2013-10-11 20:26:34 +0100
commit66c803ebcb4bc164ca5af1cf752aeb645735f186 (patch)
tree9f659782a9199c300562c8e0e56f652fcab57e87 /svgio
parentd61168908ced2e4bae32b8e0ae0a6eba6dbf37fc (diff)
Resolves: #i123379# various SVG marker corrections
(cherry picked from commit ff1eee61fc56b06e436735f5e2b133db56de3608) Conflicts: basegfx/inc/basegfx/polygon/b2dpolygontools.hxx Change-Id: I818657573a1e673e312702a4b45e7bb6394250d2
Diffstat (limited to 'svgio')
-rw-r--r--svgio/inc/svgio/svgreader/svgstyleattributes.hxx8
-rw-r--r--svgio/source/svgreader/svgstyleattributes.cxx269
2 files changed, 140 insertions, 137 deletions
diff --git a/svgio/inc/svgio/svgreader/svgstyleattributes.hxx b/svgio/inc/svgio/svgreader/svgstyleattributes.hxx
index 13e37057af41..be8ddd4e64e1 100644
--- a/svgio/inc/svgio/svgreader/svgstyleattributes.hxx
+++ b/svgio/inc/svgio/svgreader/svgstyleattributes.hxx
@@ -248,14 +248,6 @@ namespace svgio
basegfx::B2DHomMatrix& rMarkerTransform,
basegfx::B2DRange& rClipRange,
const SvgMarkerNode& rMarker) const;
- void add_singleMarker(
- drawinglayer::primitive2d::Primitive2DSequence& rTarget,
- const drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
- const basegfx::B2DHomMatrix& rMarkerTransform,
- const basegfx::B2DRange& rClipRange,
- const SvgMarkerNode& rMarker,
- const basegfx::B2DPolygon& rCandidate,
- const sal_uInt32 nIndex) const;
void add_markers(
const basegfx::B2DPolyPolygon& rPath,
drawinglayer::primitive2d::Primitive2DSequence& rTarget) const;
diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx
index 67219e7060a8..958debdfe21c 100644
--- a/svgio/source/svgreader/svgstyleattributes.cxx
+++ b/svgio/source/svgreader/svgstyleattributes.cxx
@@ -737,59 +737,6 @@ namespace svgio
}
}
- double get_markerRotation(
- const SvgMarkerNode& rMarker,
- const basegfx::B2DPolygon& rPolygon,
- const sal_uInt32 nIndex)
- {
- double fAngle(0.0);
- const sal_uInt32 nPointCount(rPolygon.count());
-
- if(nPointCount)
- {
- if(rMarker.getOrientAuto())
- {
- const bool bPrev(rPolygon.isClosed() || nIndex > 0);
- basegfx::B2DCubicBezier aSegment;
- basegfx::B2DVector aPrev;
- basegfx::B2DVector aNext;
-
- if(bPrev)
- {
- rPolygon.getBezierSegment((nIndex - 1) % nPointCount, aSegment);
- aPrev = aSegment.getTangent(1.0);
- }
-
- const bool bNext(rPolygon.isClosed() || nIndex + 1 < nPointCount);
-
- if(bNext)
- {
- rPolygon.getBezierSegment(nIndex % nPointCount, aSegment);
- aNext = aSegment.getTangent(0.0);
- }
-
- if(bPrev && bNext)
- {
- fAngle = atan2(aPrev.getY() + aNext.getY(), aPrev.getX() + aNext.getX());
- }
- else if(bPrev)
- {
- fAngle = atan2(aPrev.getY(), aPrev.getX());
- }
- else if(bNext)
- {
- fAngle = atan2(aNext.getY(), aNext.getX());
- }
- }
- else
- {
- fAngle = rMarker.getAngle();
- }
- }
-
- return fAngle;
- }
-
bool SvgStyleAttributes::prepare_singleMarker(
drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
basegfx::B2DHomMatrix& rMarkerTransform,
@@ -880,49 +827,6 @@ namespace svgio
return false;
}
- void SvgStyleAttributes::add_singleMarker(
- drawinglayer::primitive2d::Primitive2DSequence& rTarget,
- const drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
- const basegfx::B2DHomMatrix& rMarkerTransform,
- const basegfx::B2DRange& rClipRange,
- const SvgMarkerNode& rMarker,
- const basegfx::B2DPolygon& rCandidate,
- const sal_uInt32 nIndex) const
- {
- const sal_uInt32 nPointCount(rCandidate.count());
-
- if(nPointCount)
- {
- // get and apply rotation
- basegfx::B2DHomMatrix aCombinedTransform(rMarkerTransform);
- aCombinedTransform.rotate(get_markerRotation(rMarker, rCandidate, nIndex));
-
- // get and apply target position
- const basegfx::B2DPoint aPoint(rCandidate.getB2DPoint(nIndex % nPointCount));
- aCombinedTransform.translate(aPoint.getX(), aPoint.getY());
-
- // prepare marker
- drawinglayer::primitive2d::Primitive2DReference xMarker(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- aCombinedTransform,
- rMarkerPrimitives));
-
- if(!rClipRange.isEmpty())
- {
- // marker needs to be clipped, it's bigger as the mapping
- basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(rClipRange));
-
- aClipPolygon.transform(aCombinedTransform);
- xMarker = new drawinglayer::primitive2d::MaskPrimitive2D(
- aClipPolygon,
- drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1));
- }
-
- // add marker
- drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker);
- }
- }
-
void SvgStyleAttributes::add_markers(
const basegfx::B2DPolyPolygon& rPath,
drawinglayer::primitive2d::Primitive2DSequence& rTarget) const
@@ -934,51 +838,158 @@ namespace svgio
if(pStart || pMid || pEnd)
{
- const sal_uInt32 nCount(rPath.count());
+ const sal_uInt32 nSubPathCount(rPath.count());
- for (sal_uInt32 a(0); a < nCount; a++)
+ if(nSubPathCount)
{
- const basegfx::B2DPolygon aCandidate(rPath.getB2DPolygon(a));
- const sal_uInt32 nPointCount(aCandidate.count());
+ // remember prepared marker; pStart, pMid and pEnd may all be equal when
+ // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end',
+ // see 'case SVGTokenMarker' in this file; thus in this case only one common
+ // marker in primitive form will be prepared
+ const SvgMarkerNode* pPrepared = 0;
+
+ // values for the prepared marker, results of prepare_singleMarker
+ drawinglayer::primitive2d::Primitive2DSequence aPreparedMarkerPrimitives;
+ basegfx::B2DHomMatrix aPreparedMarkerTransform;
+ basegfx::B2DRange aPreparedMarkerClipRange;
- if(nPointCount)
+ for (sal_uInt32 a(0); a < nSubPathCount; a++)
{
- const sal_uInt32 nMarkerCount(aCandidate.isClosed() ? nPointCount + 1 : nPointCount);
- drawinglayer::primitive2d::Primitive2DSequence aMarkerPrimitives;
- basegfx::B2DHomMatrix aMarkerTransform;
- basegfx::B2DRange aClipRange;
- const SvgMarkerNode* pPrepared = 0;
+ // iterate over sub-paths
+ const basegfx::B2DPolygon aSubPolygonPath(rPath.getB2DPolygon(a));
+ const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count());
+ const bool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed());
- if(pStart && a==0)
+ if(nSubPolygonPointCount)
{
- if(prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pStart))
- {
- pPrepared = pStart;
- add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, 0);
- }
- }
+ // for each sub-path, create one marker per point (when closed, two markers
+ // need to pe created for the 1st point)
+ const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount);
- if(pMid)
- {
- if(pMid == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pMid))
+ for (sal_uInt32 b(0); b < nTargetMarkerCount; b++)
{
- pPrepared = pMid;
- const sal_uInt32 nFirstIndex(a==0 ? 1 : 0);
- const sal_uInt32 nLastIndex(a==nCount-1 ? nMarkerCount-1 : nMarkerCount);
+ const bool bIsFirstMarker(!a && !b);
+ const bool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b);
+ const SvgMarkerNode* pNeeded = 0;
- for(sal_uInt32 b(nFirstIndex); b < nLastIndex; b++)
+ if(bIsFirstMarker)
{
- add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, b);
+ // 1st point in 1st sub-polygon, use pStart
+ pNeeded = pStart;
+ }
+ else if(bIsLastMarker)
+ {
+ // last point in last sub-polygon, use pEnd
+ pNeeded = pEnd;
+ }
+ else
+ {
+ // anything in-between, use pMid
+ pNeeded = pMid;
}
- }
- }
- if(pEnd && a==nCount-1)
- {
- if(pEnd == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pEnd))
- {
- pPrepared = pEnd;
- add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, nMarkerCount - 1);
+ if(!pNeeded)
+ {
+ // no marker needs to be created for this point
+ continue;
+ }
+
+ if(pPrepared != pNeeded)
+ {
+ // if needed marker is not yet prepared, do it now
+ if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded))
+ {
+ pPrepared = pNeeded;
+ }
+ else
+ {
+ // error: could not prepare given marker
+ OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)");
+ pPrepared = 0;
+ continue;
+ }
+ }
+
+ // prepare complete transform
+ basegfx::B2DHomMatrix aCombinedTransform(aPreparedMarkerTransform);
+
+ // get rotation
+ if(pPrepared->getOrientAuto())
+ {
+ const sal_uInt32 nPointIndex(b % nSubPolygonPointCount);
+
+ // get entering and leaving tangents; this will search backward/froward
+ // in the polygon to find tangents unequal to zero, skipping empty edges
+ // see basegfx descriptions)
+ // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker
+ // and entering tangent for end marker. To achieve this (if wanted) it is possibe
+ // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker.
+ // This is not done here, see comment 14 in task #1232379#
+ // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
+ basegfx::B2DVector aEntering(
+ basegfx::tools::getTangentEnteringPoint(
+ aSubPolygonPath,
+ nPointIndex));
+ basegfx::B2DVector aLeaving(
+ basegfx::tools::getTangentLeavingPoint(
+ aSubPolygonPath,
+ nPointIndex));
+ const bool bEntering(!aEntering.equalZero());
+ const bool bLeaving(!aLeaving.equalZero());
+
+ if(bEntering || bLeaving)
+ {
+ basegfx::B2DVector aSum(0.0, 0.0);
+
+ if(bEntering)
+ {
+ aSum += aEntering.normalize();
+ }
+
+ if(bLeaving)
+ {
+ aSum += aLeaving.normalize();
+ }
+
+ if(!aSum.equalZero())
+ {
+ const double fAngle(atan2(aSum.getY(), aSum.getX()));
+
+ // apply rotation
+ aCombinedTransform.rotate(fAngle);
+ }
+ }
+ }
+ else
+ {
+ // apply rotation
+ aCombinedTransform.rotate(pPrepared->getAngle());
+ }
+
+ // get and apply target position
+ const basegfx::B2DPoint aPoint(aSubPolygonPath.getB2DPoint(b % nSubPolygonPointCount));
+
+ aCombinedTransform.translate(aPoint.getX(), aPoint.getY());
+
+ // prepare marker
+ drawinglayer::primitive2d::Primitive2DReference xMarker(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aCombinedTransform,
+ aPreparedMarkerPrimitives));
+
+ if(!aPreparedMarkerClipRange.isEmpty())
+ {
+ // marker needs to be clipped, it's bigger as the mapping
+ basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(aPreparedMarkerClipRange));
+
+ aClipPolygon.transform(aCombinedTransform);
+ xMarker = new drawinglayer::primitive2d::MaskPrimitive2D(
+ aClipPolygon,
+ drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1));
+ }
+
+ // add marker
+ drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker);
}
}
}