summaryrefslogtreecommitdiff
path: root/svgio/source/svgreader
diff options
context:
space:
mode:
Diffstat (limited to 'svgio/source/svgreader')
-rw-r--r--svgio/source/svgreader/svgcirclenode.cxx2
-rw-r--r--svgio/source/svgreader/svgsvgnode.cxx614
-rw-r--r--svgio/source/svgreader/svgtools.cxx65
3 files changed, 480 insertions, 201 deletions
diff --git a/svgio/source/svgreader/svgcirclenode.cxx b/svgio/source/svgreader/svgcirclenode.cxx
index 6dc269ca867a..c742a41f9989 100644
--- a/svgio/source/svgreader/svgcirclenode.cxx
+++ b/svgio/source/svgreader/svgcirclenode.cxx
@@ -122,7 +122,7 @@ namespace svgio
if(pStyle && getR().isSet())
{
- const double fR(getR().solve(*this, xcoordinate));
+ const double fR(getR().solve(*this, length));
if(fR > 0.0)
{
diff --git a/svgio/source/svgreader/svgsvgnode.cxx b/svgio/source/svgreader/svgsvgnode.cxx
index a8daee667ebe..8524a016c3e5 100644
--- a/svgio/source/svgreader/svgsvgnode.cxx
+++ b/svgio/source/svgreader/svgsvgnode.cxx
@@ -39,7 +39,7 @@ namespace svgio
: SvgNode(SVGTokenSvg, rDocument, pParent),
maSvgStyleAttributes(*this),
mpViewBox(0),
- maSvgAspectRatio(SvgAspectRatio(Align_xMidYMid, false, true)),
+ maSvgAspectRatio(),
maX(),
maY(),
maWidth(),
@@ -157,6 +157,91 @@ namespace svgio
}
}
+ void SvgSvgNode::seekReferenceWidth(double& fWidth, bool& bHasFound) const
+ {
+ if (!getParent() || bHasFound)
+ {
+ return;
+ }
+ const SvgSvgNode* pParentSvgSvgNode = 0;
+ // enclosing svg might have relative width, need to cumulate them till they are
+ // resolved somewhere up in the node tree
+ double fPercentage(1.0);
+ for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
+ {
+ // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
+ pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
+ if (pParentSvgSvgNode)
+ {
+ if (pParentSvgSvgNode->getViewBox())
+ {
+ // viewbox values are already in 'user unit'.
+ fWidth = pParentSvgSvgNode->getViewBox()->getWidth() * fPercentage;
+ bHasFound = true;
+ }
+ else
+ {
+ // take absolute value or cummulate percentage
+ if (pParentSvgSvgNode->getWidth().isSet())
+ {
+ if (Unit_percent == pParentSvgSvgNode->getWidth().getUnit())
+ {
+ fPercentage *= pParentSvgSvgNode->getWidth().getNumber() * 0.01;
+ }
+ else
+ {
+ fWidth = pParentSvgSvgNode->getWidth().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
+ bHasFound = true;
+ }
+ } // not set => width=100% => factor 1, no need for else
+ }
+ }
+ }
+ }
+
+ void SvgSvgNode::seekReferenceHeight(double& fHeight, bool& bHasFound) const
+ {
+ if (!getParent() || bHasFound)
+ {
+ return;
+ }
+ const SvgSvgNode* pParentSvgSvgNode = 0;
+ // enclosing svg might have relative width and height, need to cumulate them till they are
+ // resolved somewhere up in the node tree
+ double fPercentage(1.0);
+ for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
+ {
+ // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
+ pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
+ if (pParentSvgSvgNode)
+ {
+ if (pParentSvgSvgNode->getViewBox())
+ {
+ // viewbox values are already in 'user unit'.
+ fHeight = pParentSvgSvgNode->getViewBox()->getHeight() * fPercentage;
+ bHasFound = true;
+ }
+ else
+ {
+ // take absolute value or cummulate percentage
+ if (pParentSvgSvgNode->getHeight().isSet())
+ {
+ if (Unit_percent == pParentSvgSvgNode->getHeight().getUnit())
+ {
+ fPercentage *= pParentSvgSvgNode->getHeight().getNumber() * 0.01;
+ }
+ else
+ {
+ fHeight = pParentSvgSvgNode->getHeight().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
+ bHasFound = true;
+ }
+ } // not set => height=100% => factor 1, no need for else
+ }
+ }
+ }
+ }
+
+// ToDo: Consider attribute overflow in method decomposeSvgNode
void SvgSvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
{
drawinglayer::primitive2d::Primitive2DSequence aSequence;
@@ -168,13 +253,7 @@ namespace svgio
{
if(getParent())
{
- const bool bWidthIsRelative(!getWidth().isSet() || Unit_percent == getWidth().getUnit());
- const bool bHeightIsRelative(!getWidth().isSet() || Unit_percent == getWidth().getUnit());
- const SvgSvgNode* pParentSvgSvgNode = 0;
- double fW(0.0);
- double fH(0.0);
-
- // #i122594# if width/height is not given, it's 100% (see 5.1.2 The svg element in SVG1.1 spec).
+ // #i122594# if width/height is not given, it's 100% (see 5.1.2 The 'svg' element in SVG1.1 spec).
// If it is relative, the question is to what. The previous implementatin assumed relative to the
// local ViewBox which is implied by (4.2 Basic data types):
//
@@ -185,56 +264,92 @@ namespace svgio
// units in general), and (b) when a percentage length value represents a percentage of the
// bounding box width or height on a given object (refer to the section that describes object
// bounding box units)."
- //
- // This is not closer specified for the SVG element itself as non-outmost element, but comparisons
- // with common browsers shows that it's mostly interpreted relative to the viewBox of the parent.
- // Adding code to search the parent SVG element and calculating width/height relative to it's
- // viewBox width/height (and no longer to the local viewBox).
- if(bWidthIsRelative || bHeightIsRelative)
+
+ // Comparisons with commom browsers show, that it's mostly interpreted relative to the viewport
+ // of the parent, and so does the new implementation.
+
+ // Extract known viewport data
+ // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
+
+ // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
+ // value 0.0 here is only to initialize variable
+ bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
+ double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
+
+ bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
+ double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
+
+ // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
+ bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
+ double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
+
+ bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
+ double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
+
+ if ( !bXIsAbsolute || !bWidthIsAbsolute)
{
- for(const SvgNode* pParent = getParent(); pParent && !pParentSvgSvgNode; pParent = pParent->getParent())
+ // get width of enclosing svg and resolve percentage in x and width;
+ double fWReference(0.0);
+ bool bHasFoundWidth(false);
+ seekReferenceWidth(fWReference, bHasFoundWidth);
+ if (!bHasFoundWidth)
{
- pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
+ // Even outermost svg has not all information to resolve relative values,
+ // I use content itself as fallback to set missing values for viewport
+ // Any better idea for such ill structures svg documents?
+ const basegfx::B2DRange aChildRange(
+ drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
+ aSequence,
+ drawinglayer::geometry::ViewInformation2D()));
+ fWReference = aChildRange.getWidth();
}
- }
-
- if(bWidthIsRelative)
- {
- fW = getWidth().isSet() ? getWidth().getNumber() * 0.01 : 1.0;
-
- if(pParentSvgSvgNode)
+ // referenced values are already in 'user unit'
+ if (!bXIsAbsolute)
{
- fW *= pParentSvgSvgNode->getViewBox()->getWidth();
+ fX = getX().getNumber() * 0.01 * fWReference;
+ }
+ if (!bWidthIsAbsolute)
+ {
+ fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
}
- }
- else
- {
- fW = getWidth().solve(*this, xcoordinate);
}
- if(bHeightIsRelative)
+ if ( !bYIsAbsolute || !bHeightIsAbsolute)
{
- fH = getHeight().isSet() ? getHeight().getNumber() * 0.01 : 1.0;
+ // get height of enclosing svg and resolve percentage in y and height
+ double fHReference(0.0);
+ bool bHasFoundHeight(false);
+ seekReferenceHeight(fHReference, bHasFoundHeight);
+ if (!bHasFoundHeight)
+ {
+ // Even outermost svg has not all information to resolve relative values,
+ // I use content itself as fallback to set missing values for viewport
+ // Any better idea for such ill structures svg documents?
+ const basegfx::B2DRange aChildRange(
+ drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
+ aSequence,
+ drawinglayer::geometry::ViewInformation2D()));
+ fHReference = aChildRange.getHeight();
+ }
- if(pParentSvgSvgNode)
+ // referenced values are already in 'user unit'
+ if (!bYIsAbsolute)
{
- fH *= pParentSvgSvgNode->getViewBox()->getHeight();
+ fY = getY().getNumber() * 0.01 * fHReference;
+ }
+ if (!bHeightIsAbsolute)
+ {
+ fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
}
- }
- else
- {
- fH = getHeight().solve(*this, ycoordinate);
}
if(getViewBox())
{
- // Svg defines that with no width or no height the viewBox content is empty,
- // so both need to exist
- if(!basegfx::fTools::equalZero(getViewBox()->getWidth()) && !basegfx::fTools::equalZero(getViewBox()->getHeight()))
+ // SVG 1.1 defines in section 7.7 that a negative value for width or height
+ // in viewBox is an error and that 0.0 disables rendering
+ if(basegfx::fTools::more(getViewBox()->getWidth(),0.0) && basegfx::fTools::more(getViewBox()->getHeight(),0.0))
{
- // create target range homing x,y, width and height as given
- const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
- const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
+ // create target range homing x,y, width and height as calculated above
const basegfx::B2DRange aTarget(fX, fY, fX + fW, fY + fH);
if(aTarget.equal(*getViewBox()))
@@ -245,63 +360,44 @@ namespace svgio
else
{
// create mapping
- const SvgAspectRatio& rRatio = getSvgAspectRatio();
+ // #i122610 SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
+ // then the effect is as if a value of 'xMidYMid meet' were specified.
+ SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
+ const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
+
+ // let mapping be created from SvgAspectRatio
+ const basegfx::B2DHomMatrix aEmbeddingTransform(
+ rRatio.createMapping(aTarget, *getViewBox()));
+
+ // prepare embedding in transformation
+ const drawinglayer::primitive2d::Primitive2DReference xRef(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aEmbeddingTransform,
+ aSequence));
- if(rRatio.isSet())
+ if(rRatio.isMeetOrSlice())
{
- // let mapping be created from SvgAspectRatio
- const basegfx::B2DHomMatrix aEmbeddingTransform(
- rRatio.createMapping(aTarget, *getViewBox()));
-
- // prepare embedding in transformation
- const drawinglayer::primitive2d::Primitive2DReference xRef(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- aEmbeddingTransform,
- aSequence));
-
- if(rRatio.isMeetOrSlice())
- {
- // embed in transformation
- drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
- }
- else
- {
- // need to embed in MaskPrimitive2D, too
- const drawinglayer::primitive2d::Primitive2DReference xMask(
- new drawinglayer::primitive2d::MaskPrimitive2D(
- basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aTarget)),
- drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1)));
-
- drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
- }
+ // embed in transformation
+ drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
}
else
{
- // choose default mapping
- const basegfx::B2DHomMatrix aEmbeddingTransform(
- rRatio.createLinearMapping(
- aTarget, *getViewBox()));
-
- // embed in transformation
- const drawinglayer::primitive2d::Primitive2DReference xTransform(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- aEmbeddingTransform,
- aSequence));
+ // need to embed in MaskPrimitive2D, too
+ const drawinglayer::primitive2d::Primitive2DReference xMask(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aTarget)),
+ drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1)));
- drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xTransform);
+ drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
}
}
}
}
- else
+ else // no viewBox attribute
{
// Svg defines that a negative value is an error and that 0.0 disables rendering
if(basegfx::fTools::more(fW, 0.0) && basegfx::fTools::more(fH, 0.0))
{
- // check if we have a x,y position
- const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
- const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
-
if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
{
// embed in transform
@@ -326,140 +422,284 @@ namespace svgio
}
}
}
- else
+ else // Outermost SVG element
{
- // Outermost SVG element; create target range homing width and height as given.
- // SVG defines that x,y has no meaning for the outermost SVG element. Use a fallback
- // width and height of DIN A 4 (21 x 29,7 cm)
- double fViewPortW(getWidth().isSet() ? getWidth().solve(*this, xcoordinate) : (210.0 * 3.543307));
- double fViewPortH(getHeight().isSet() ? getHeight().solve(*this, ycoordinate) : (297.0 * 3.543307));
- basegfx::B2DRange aSvgCanvasRange(0.0, 0.0, 0.0, 0.0);
+ double fW = 0.0; // effective value depends on viewBox
+ double fH = 0.0;
// Svg defines that a negative value is an error and that 0.0 disables rendering
- if(basegfx::fTools::more(fViewPortW, 0.0) && basegfx::fTools::more(fViewPortH, 0.0))
+ // isPositive() not usable because it allows 0.0 in contrast to mathematical definition of 'positive'
+ const bool bWidthInvalid(getWidth().isSet() && basegfx::fTools::lessOrEqual(getWidth().getNumber(), 0.0));
+ const bool bHeightInvalid(getHeight().isSet() && basegfx::fTools::lessOrEqual(getHeight().getNumber(), 0.0));
+ if(!bWidthInvalid && !bHeightInvalid)
{
- basegfx::B2DRange aContentRange; // ViewBox or bounding box
+ basegfx::B2DRange aSvgCanvasRange; // effective value depends on viewBox
if(getViewBox())
{
- aContentRange = *getViewBox();
+ // SVG 1.1 defines in section 7.7 that a negative value for width or height
+ // in viewBox is an error and that 0.0 disables rendering
+ const double fViewBoxWidth = getViewBox()->getWidth();
+ const double fViewBoxHeight = getViewBox()->getHeight();
+ if(basegfx::fTools::more(fViewBoxWidth,0.0) && basegfx::fTools::more(fViewBoxHeight,0.0))
+ {
+ // The intrinsic aspect ratio of the svg element is given by absolute values of both width and height
+ // or if one or both of them is relative by the width and height of the viewBox
+ // see SVG 1.1 section 7.12
+ const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
+ const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
+ if(bWidthIsAbsolute && bHeightIsAbsolute)
+ {
+ fW =getWidth().solveNonPercentage(*this);
+ fH =getHeight().solveNonPercentage(*this);
+ }
+ else if (bWidthIsAbsolute)
+ {
+ fW = getWidth().solveNonPercentage(*this);
+ fH = fW * fViewBoxWidth / fViewBoxHeight ;
+ }
+ else if (bHeightIsAbsolute)
+ {
+ fH = getHeight().solveNonPercentage(*this);
+ fW = fH * fViewBoxWidth / fViewBoxHeight ;
+ }
+ else
+ {
+ fW = fViewBoxWidth;
+ fH = fViewBoxHeight;
+ }
+ // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
+ aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
+
+ // create mapping
+ // SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
+ // then the effect is as if a value of 'xMidYMid meet' were specified.
+ SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
+ const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
+
+ basegfx::B2DHomMatrix aViewBoxMapping;
+ aViewBoxMapping = rRatio.createMapping(aSvgCanvasRange, *getViewBox());
+ // no need to check ratio here for slice, the outermost Svg will
+ // be clipped anyways (see below)
+
+ // scale content to viewBox definitions
+ const drawinglayer::primitive2d::Primitive2DReference xTransform(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aViewBoxMapping,
+ aSequence));
+
+ aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
+ }
}
- else // no ViewBox given -> get bounding box TODO: ignore preserveAspectRatio i.e. set to default?
+ else // no viewbox
{
- aContentRange = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(aSequence, drawinglayer::geometry::ViewInformation2D());
+ // There exists no parent to resolve relative width or height.
+ // Use child size as fallback.
+ const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
+ const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
+ if (bWidthIsAbsolute && bHeightIsAbsolute)
+ {
+ fW =getWidth().solveNonPercentage(*this);
+ fH =getHeight().solveNonPercentage(*this);
+
+ }
+ else
+ {
+ const basegfx::B2DRange aChildRange(
+ drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
+ aSequence,
+ drawinglayer::geometry::ViewInformation2D()));
+ const double fChildWidth(aChildRange.getWidth());
+ const double fChildHeight(aChildRange.getHeight());
+ fW = bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : fChildWidth;
+ fH = bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : fChildHeight;
+ }
+ // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
+ aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
}
- double fW(aContentRange.getWidth());
- double fH(aContentRange.getHeight());
- if(!basegfx::fTools::equalZero(fW) && !basegfx::fTools::equalZero(fH))
+ // to be completely correct in Svg sense it is necessary to clip
+ // the whole content to the given canvas. I choose here to do this
+ // initially despite I found various examples of Svg files out there
+ // which have no correct values for this clipping. It's correct
+ // due to the Svg spec.
+ bool bDoCorrectCanvasClipping(true);
+
+ if(bDoCorrectCanvasClipping)
{
- const SvgAspectRatio& rRatio = getSvgAspectRatio();
- double fScale = std::min(fViewPortW/fW, fViewPortH/fH);
- fW *= fScale;
- fH *= fScale;
- aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
+ // different from Svg we have the possibility with primitives to get
+ // a correct bounding box for the geometry. Get it for evtl. taking action
+ const basegfx::B2DRange aContentRange(
+ drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
+ aSequence,
+ drawinglayer::geometry::ViewInformation2D()));
+
+ if(aSvgCanvasRange.isInside(aContentRange))
+ {
+ // no clip needed, but an invisible HiddenGeometryPrimitive2D
+ // to allow getting the full Svg range using the primitive mechanisms.
+ // This is needed since e.g. an SdrObject using this as graphic will
+ // create a mapping transformation to exactly map the content to it's
+ // real life size
+ const drawinglayer::primitive2d::Primitive2DReference xLine(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ basegfx::tools::createPolygonFromRect(
+ aSvgCanvasRange),
+ basegfx::BColor(0.0, 0.0, 0.0)));
+ const drawinglayer::primitive2d::Primitive2DReference xHidden(
+ new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(
+ drawinglayer::primitive2d::Primitive2DSequence(&xLine, 1)));
+
+ drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aSequence, xHidden);
+ }
+ else if(aSvgCanvasRange.overlaps(aContentRange))
+ {
+ // Clip is necessary. This will make Svg images evtl. smaller
+ // than wanted from Svg (the free space which may be around it is
+ // conform to the Svg spec), but avoids an expensive and unneccessary
+ // clip. Keep the full Svg range here to get the correct mappings
+ // to objects using this. Optimizations can be done in the processors
+ const drawinglayer::primitive2d::Primitive2DReference xMask(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::tools::createPolygonFromRect(
+ aSvgCanvasRange)),
+ aSequence));
- // let mapping be created from SvgAspectRatio
- basegfx::B2DHomMatrix aViewBoxMapping;
- aViewBoxMapping = rRatio.createMapping(aSvgCanvasRange, aContentRange);
+ aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
+ }
+ else
+ {
+ // not inside, no overlap. Empty Svg
+ aSequence.realloc(0);
+ }
+ }
- // no need to check ratio here for slice, the outermost Svg will
- // be clipped anyways (see below)
+ if(aSequence.hasElements())
+ {
+ // embed in transform primitive to scale to 1/100th mm
+ // where 1 inch == 25.4 mm to get from Svg coordinates (px) to
+ // drawinglayer coordinates
+ const double fScaleTo100thmm(25.4 * 100.0 / F_SVG_PIXEL_PER_INCH);
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::tools::createScaleB2DHomMatrix(
+ fScaleTo100thmm,
+ fScaleTo100thmm));
- // scale content to viewBox definitions
const drawinglayer::primitive2d::Primitive2DReference xTransform(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- aViewBoxMapping,
- aSequence));
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTransform,
+ aSequence));
aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
+
+ // append to result
+ drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
}
}
- // to be completely correct in Svg sense it is necessary to clip
- // the whole content to the given canvas. I choose here to do this
- // initially despite I found various examples of Svg files out there
- // which have no correct values for this clipping. It's correct
- // due to the Svg spec.
- bool bDoCorrectCanvasClipping(true);
-
- if(bDoCorrectCanvasClipping)
+ }
+ }
+ }
+
+ const basegfx::B2DRange* SvgSvgNode::getCurrentViewPort() const
+ {
+ if(getViewBox())
+ {
+ return getViewBox();
+ }
+ else // viewport should be given by x, y, width, and height
+ {
+ // Extract known viewport data
+ // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
+ if (getParent())
+ {
+ // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
+ // value 0.0 here is only to initialize variable
+ bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
+ double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
+ bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
+ double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
+
+ // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
+ bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
+ double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
+
+ bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
+ double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
+
+ if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
{
- // different from Svg we have the possibility with primitives to get
- // a correct bounding box for the geometry. Get it for evtl. taking action
- const basegfx::B2DRange aContentRange(
- drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
- aSequence,
- drawinglayer::geometry::ViewInformation2D()));
-
- if(aSvgCanvasRange.isInside(aContentRange))
+ return &basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
+ }
+ else // try to resolve relative values
+ {
+ if (!bXIsAbsolute || !bWidthIsAbsolute)
{
- // no clip needed, but an invisible HiddenGeometryPrimitive2D
- // to allow getting the full Svg range using the primitive mechanisms.
- // This is needed since e.g. an SdrObject using this as graphic will
- // create a mapping transformation to exactly map the content to it's
- // real life size
- const drawinglayer::primitive2d::Primitive2DReference xLine(
- new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
- basegfx::tools::createPolygonFromRect( aSvgCanvasRange),
- basegfx::BColor(0.0, 0.0, 0.0)));
- const drawinglayer::primitive2d::Primitive2DReference xHidden(
- new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(
- drawinglayer::primitive2d::Primitive2DSequence(&xLine, 1)));
-
- drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aSequence, xHidden);
+ // get width of enclosing svg and resolve percentage in x and width
+ double fWReference(0.0);
+ bool bHasFoundWidth(false);
+ seekReferenceWidth(fWReference, bHasFoundWidth);
+ // referenced values are already in 'user unit'
+ if (!bXIsAbsolute && bHasFoundWidth)
+ {
+ fX = getX().getNumber() * 0.01 * fWReference;
+ bXIsAbsolute = true;
+ }
+ if (!bWidthIsAbsolute && bHasFoundWidth)
+ {
+ fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
+ bWidthIsAbsolute = true;
+ }
}
- else if(aSvgCanvasRange.overlaps(aContentRange))
+ if (!bYIsAbsolute || !bHeightIsAbsolute)
{
- // Clip is necessary. This will make Svg images evtl. smaller
- // than wanted from Svg (the free space which may be around it is
- // conform to the Svg spec), but avoids an expensive and unneccessary
- // clip. Keep the full Svg range here to get the correct mappings
- // to objects using this. Optimizations can be done in the processors
- const drawinglayer::primitive2d::Primitive2DReference xMask(
- new drawinglayer::primitive2d::MaskPrimitive2D(
- basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aSvgCanvasRange)),
- aSequence));
+ // get height of enclosing svg and resolve percentage in y and height
+ double fHReference(0.0);
+ bool bHasFoundHeight(false);
+ seekReferenceHeight(fHReference, bHasFoundHeight);
+ // referenced values are already in 'user unit'
+ if (!bYIsAbsolute && bHasFoundHeight)
+ {
+ fY = getY().getNumber() * 0.01 * fHReference;
+ bYIsAbsolute = true;
+ }
+ if (!bHeightIsAbsolute && bHasFoundHeight)
+ {
+ fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
+ bHeightIsAbsolute = true;
+ }
+ }
- aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
+ if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
+ {
+ return &basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
}
- else
+ else // relative values could not be resolved, there exists no fallback
{
- // not inside, no overlap. Empty Svg
- aSequence.realloc(0);
+ return SvgNode::getCurrentViewPort();
}
}
-
- if(aSequence.hasElements())
+ }
+ else //outermost svg
+ {
+ // If width or height is not provided, the default would be 100%, see SVG 1.1 section 5.1.2
+ // But here it cannot be resolved and no fallback exists.
+ // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
+ bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
+ double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
+ bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
+ double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
+ if (bWidthIsAbsolute && bHeightIsAbsolute)
+ {
+ return &basegfx::B2DRange(0.0, 0.0, fW, fH);
+ }
+ else // no fallback exists
{
- // embed in transform primitive to scale to 1/100th mm
- // where 1 mm == 3.543307 px to get from Svg coordinates to
- // drawinglayer ones
- const double fScaleTo100thmm(100.0 / 3.543307);
- const basegfx::B2DHomMatrix aTransform(
- basegfx::tools::createScaleB2DHomMatrix(
- fScaleTo100thmm,
- fScaleTo100thmm));
-
- const drawinglayer::primitive2d::Primitive2DReference xTransform(
- new drawinglayer::primitive2d::TransformPrimitive2D( aTransform, aSequence));
-
- aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
-
- // append to result
- drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
+ return SvgNode::getCurrentViewPort();
}
}
- }
- }
+// ToDo: Is it possible to decompose and use the bounding box of the childs, if even the
+// outermost svg has no information to resolve percentage? Is it worth, how expensive is it?
- const basegfx::B2DRange* SvgSvgNode::getCurrentViewPort() const
- {
- if(getViewBox())
- {
- return getViewBox();
- }
- else
- {
- return SvgNode::getCurrentViewPort();
}
}
diff --git a/svgio/source/svgreader/svgtools.cxx b/svgio/source/svgreader/svgtools.cxx
index 47b6e49a350d..ec74902475c9 100644
--- a/svgio/source/svgreader/svgtools.cxx
+++ b/svgio/source/svgreader/svgtools.cxx
@@ -151,7 +151,7 @@ namespace svgio
return aRetval;
}
- double SvgNumber::solve(const InfoProvider& rInfoProvider, NumberType aNumberType) const
+ double SvgNumber::solveNonPercentage(const InfoProvider& rInfoProvider) const
{
if(isSet())
{
@@ -179,17 +179,53 @@ namespace svgio
switch(meUnit)
{
- case Unit_pt: fRetval *= 1.25; break;
- case Unit_pc: fRetval *= 15.0; break;
- case Unit_cm: fRetval *= 35.43307; break;
- case Unit_mm: fRetval *= 3.543307; break;
- case Unit_in: fRetval *= 90.0; break;
+ case Unit_pt: fRetval *= F_SVG_PIXEL_PER_INCH / 72.0; break;
+ case Unit_pc: fRetval *= F_SVG_PIXEL_PER_INCH / 6.0; break;
+ case Unit_cm: fRetval *= F_SVG_PIXEL_PER_INCH / 2.54; break;
+ case Unit_mm: fRetval *= 0.1 * F_SVG_PIXEL_PER_INCH / 2.54; break;
+ case Unit_in: fRetval *= F_SVG_PIXEL_PER_INCH; break;
default: break;
}
return fRetval;
break;
}
+ default:
+ {
+ OSL_ENSURE(false, "Do not use with percentage! ");
+ return 0.0;
+ break;
+ }
+ }
+ }
+
+ /// not set
+ OSL_ENSURE(false, "SvgNumber not set (!)");
+ return 0.0;
+ }
+
+ double SvgNumber::solve(const InfoProvider& rInfoProvider, NumberType aNumberType) const
+ {
+ if(isSet())
+ {
+ switch(meUnit)
+ {
+ case Unit_px:
+ {
+ return mfNumber;
+ break;
+ }
+ case Unit_pt:
+ case Unit_pc:
+ case Unit_cm:
+ case Unit_mm:
+ case Unit_in:
+ case Unit_em:
+ case Unit_ex:
+ {
+ return solveNonPercentage( rInfoProvider);
+ break;
+ }
case Unit_percent:
{
double fRetval(mfNumber * 0.01);
@@ -197,12 +233,15 @@ namespace svgio
if(!pViewPort)
{
+#ifdef DBG_UTIL
+ myAssert(rtl::OUString::createFromAscii("Design error, this case should have been handled in the caller"));
+#endif
// no viewPort, assume a normal page size (A4)
static basegfx::B2DRange aDinA4Range(
0.0,
0.0,
- 210.0 * 3.543307,
- 297.0 * 3.543307);
+ 210.0 * F_SVG_PIXEL_PER_INCH / 2.54,
+ 297.0 * F_SVG_PIXEL_PER_INCH / 2.54);
pViewPort = &aDinA4Range;
}
@@ -920,11 +959,11 @@ namespace svgio
if(readNumberAndUnit(rCandidate, nPos, aHeight, nLen))
{
- return basegfx::B2DRange(
- aMinX.solve(rInfoProvider, xcoordinate),
- aMinY.solve(rInfoProvider, ycoordinate),
- aWidth.solve(rInfoProvider, xcoordinate),
- aHeight.solve(rInfoProvider, ycoordinate));
+ double fX(aMinX.solve(rInfoProvider, xcoordinate));
+ double fY(aMinY.solve(rInfoProvider, ycoordinate));
+ double fW(aWidth.solve(rInfoProvider,xcoordinate));
+ double fH(aHeight.solve(rInfoProvider,ycoordinate));
+ return basegfx::B2DRange(fX,fY,fX+fW,fY+fH);
}
}
}