Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <svgio/svgreader/svgsvgnode.hxx>
21 : #include <drawinglayer/geometry/viewinformation2d.hxx>
22 : #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
23 : #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
24 : #include <basegfx/polygon/b2dpolygontools.hxx>
25 : #include <basegfx/polygon/b2dpolygon.hxx>
26 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
27 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
28 : #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
29 :
30 : namespace svgio
31 : {
32 : namespace svgreader
33 : {
34 299 : SvgSvgNode::SvgSvgNode(
35 : SvgDocument& rDocument,
36 : SvgNode* pParent)
37 : : SvgNode(SVGTokenSvg, rDocument, pParent),
38 : maSvgStyleAttributes(*this),
39 : mpViewBox(0),
40 : maSvgAspectRatio(),
41 : maX(),
42 : maY(),
43 : maWidth(),
44 : maHeight(),
45 : maVersion(),
46 299 : mbStyleAttributesInitialized(false) // #i125258#
47 : {
48 299 : }
49 :
50 : // #i125258#
51 299 : void SvgSvgNode::initializeStyleAttributes()
52 : {
53 299 : if(!mbStyleAttributesInitialized)
54 : {
55 : // #i125258# determine if initial values need to be initialized with hard values
56 : // for the case that this is the outmost SVG statement and it has no parent
57 : // stale (CssStyle for svg may be defined)
58 299 : bool bSetInitialValues(true);
59 :
60 299 : if(getParent())
61 : {
62 : // #i125258# no initial values when it's a SVG element embedded in SVG
63 0 : bSetInitialValues = false;
64 : }
65 :
66 299 : if(bSetInitialValues)
67 : {
68 299 : const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
69 :
70 299 : if(pStyles && pStyles->getParentStyle())
71 : {
72 : // SVG has a parent style (probably CssStyle), check if fill is set there anywhere
73 : // already. If yes, do not set the default fill (black)
74 13 : bool bFillSet(false);
75 13 : const SvgStyleAttributes* pParentStyle = pStyles->getParentStyle();
76 :
77 39 : while(pParentStyle && !bFillSet)
78 : {
79 13 : bFillSet = pParentStyle->isFillSet();
80 13 : pParentStyle = pParentStyle->getParentStyle();
81 : }
82 :
83 13 : if(bFillSet)
84 : {
85 : // #125258# no initial values when SVG has a parent style at which a fill
86 : // is already set
87 0 : bSetInitialValues = false;
88 : }
89 : }
90 : }
91 :
92 299 : if(bSetInitialValues)
93 : {
94 : // #i125258# only set if not yet initialized (SvgSvgNode::parseAttribute is already done,
95 : // just setting may revert an already set valid value)
96 299 : if(!maSvgStyleAttributes.isFillSet())
97 : {
98 : // #i125258# initial fill is black (see SVG1.1 spec)
99 299 : maSvgStyleAttributes.setFill(SvgPaint(basegfx::BColor(0.0, 0.0, 0.0), true, true));
100 : }
101 : }
102 :
103 299 : mbStyleAttributesInitialized = true;
104 : }
105 299 : }
106 :
107 897 : SvgSvgNode::~SvgSvgNode()
108 : {
109 299 : if(mpViewBox) delete mpViewBox;
110 598 : }
111 :
112 29885 : const SvgStyleAttributes* SvgSvgNode::getSvgStyleAttributes() const
113 : {
114 : // #i125258# svg node can have CssStyles, too, so check for it here
115 29885 : return checkForCssStyle(OUString("svg"), maSvgStyleAttributes);
116 : }
117 :
118 3069 : void SvgSvgNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
119 : {
120 : // call parent
121 3069 : SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
122 :
123 : // read style attributes
124 3069 : maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent, false);
125 :
126 : // parse own
127 3069 : switch(aSVGToken)
128 : {
129 : case SVGTokenStyle:
130 : {
131 12 : readLocalCssStyle(aContent);
132 12 : break;
133 : }
134 : case SVGTokenViewBox:
135 : {
136 295 : const basegfx::B2DRange aRange(readViewBox(aContent, *this));
137 :
138 295 : if(!aRange.isEmpty())
139 : {
140 295 : setViewBox(&aRange);
141 : }
142 295 : break;
143 : }
144 : case SVGTokenPreserveAspectRatio:
145 : {
146 0 : setSvgAspectRatio(readSvgAspectRatio(aContent));
147 0 : break;
148 : }
149 : case SVGTokenX:
150 : {
151 0 : SvgNumber aNum;
152 :
153 0 : if(readSingleNumber(aContent, aNum))
154 : {
155 0 : setX(aNum);
156 : }
157 0 : break;
158 : }
159 : case SVGTokenY:
160 : {
161 0 : SvgNumber aNum;
162 :
163 0 : if(readSingleNumber(aContent, aNum))
164 : {
165 0 : setY(aNum);
166 : }
167 0 : break;
168 : }
169 : case SVGTokenWidth:
170 : {
171 299 : SvgNumber aNum;
172 :
173 299 : if(readSingleNumber(aContent, aNum))
174 : {
175 299 : if(aNum.isPositive())
176 : {
177 299 : setWidth(aNum);
178 : }
179 : }
180 299 : break;
181 : }
182 : case SVGTokenHeight:
183 : {
184 299 : SvgNumber aNum;
185 :
186 299 : if(readSingleNumber(aContent, aNum))
187 : {
188 299 : if(aNum.isPositive())
189 : {
190 299 : setHeight(aNum);
191 : }
192 : }
193 299 : break;
194 : }
195 : case SVGTokenVersion:
196 : {
197 295 : SvgNumber aNum;
198 :
199 295 : if(readSingleNumber(aContent, aNum))
200 : {
201 295 : setVersion(aNum);
202 : }
203 295 : break;
204 : }
205 : default:
206 : {
207 1869 : break;
208 : }
209 : }
210 3069 : }
211 :
212 0 : void SvgSvgNode::seekReferenceWidth(double& fWidth, bool& bHasFound) const
213 : {
214 0 : if (!getParent() || bHasFound)
215 : {
216 0 : return;
217 : }
218 0 : const SvgSvgNode* pParentSvgSvgNode = 0;
219 : // enclosing svg might have relative width, need to cumulate them till they are
220 : // resolved somewhere up in the node tree
221 0 : double fPercentage(1.0);
222 0 : for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
223 : {
224 : // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
225 0 : pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
226 0 : if (pParentSvgSvgNode)
227 : {
228 0 : if (pParentSvgSvgNode->getViewBox())
229 : {
230 : // viewbox values are already in 'user unit'.
231 0 : fWidth = pParentSvgSvgNode->getViewBox()->getWidth() * fPercentage;
232 0 : bHasFound = true;
233 : }
234 : else
235 : {
236 : // take absolute value or cummulate percentage
237 0 : if (pParentSvgSvgNode->getWidth().isSet())
238 : {
239 0 : if (Unit_percent == pParentSvgSvgNode->getWidth().getUnit())
240 : {
241 0 : fPercentage *= pParentSvgSvgNode->getWidth().getNumber() * 0.01;
242 : }
243 : else
244 : {
245 0 : fWidth = pParentSvgSvgNode->getWidth().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
246 0 : bHasFound = true;
247 : }
248 : } // not set => width=100% => factor 1, no need for else
249 : }
250 : }
251 : }
252 : }
253 :
254 0 : void SvgSvgNode::seekReferenceHeight(double& fHeight, bool& bHasFound) const
255 : {
256 0 : if (!getParent() || bHasFound)
257 : {
258 0 : return;
259 : }
260 0 : const SvgSvgNode* pParentSvgSvgNode = 0;
261 : // enclosing svg might have relative width and height, need to cumulate them till they are
262 : // resolved somewhere up in the node tree
263 0 : double fPercentage(1.0);
264 0 : for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
265 : {
266 : // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
267 0 : pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
268 0 : if (pParentSvgSvgNode)
269 : {
270 0 : if (pParentSvgSvgNode->getViewBox())
271 : {
272 : // viewbox values are already in 'user unit'.
273 0 : fHeight = pParentSvgSvgNode->getViewBox()->getHeight() * fPercentage;
274 0 : bHasFound = true;
275 : }
276 : else
277 : {
278 : // take absolute value or cummulate percentage
279 0 : if (pParentSvgSvgNode->getHeight().isSet())
280 : {
281 0 : if (Unit_percent == pParentSvgSvgNode->getHeight().getUnit())
282 : {
283 0 : fPercentage *= pParentSvgSvgNode->getHeight().getNumber() * 0.01;
284 : }
285 : else
286 : {
287 0 : fHeight = pParentSvgSvgNode->getHeight().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
288 0 : bHasFound = true;
289 : }
290 : } // not set => height=100% => factor 1, no need for else
291 : }
292 : }
293 : }
294 : }
295 :
296 : // ToDo: Consider attribute overflow in method decomposeSvgNode
297 299 : void SvgSvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
298 : {
299 299 : drawinglayer::primitive2d::Primitive2DSequence aSequence;
300 :
301 : // #i125258# check now if we need to init some style settings locally. Do not do this
302 : // in the constructor, there is not yet informatikon e.g. about existing CssStyles.
303 : // Here all nodes are read and interpreted
304 299 : const_cast< SvgSvgNode* >(this)->initializeStyleAttributes();
305 :
306 : // decompose children
307 299 : SvgNode::decomposeSvgNode(aSequence, bReferenced);
308 :
309 299 : if(aSequence.hasElements())
310 : {
311 299 : if(getParent())
312 : {
313 : // #i122594# if width/height is not given, it's 100% (see 5.1.2 The 'svg' element in SVG1.1 spec).
314 : // If it is relative, the question is to what. The previous implementatin assumed relative to the
315 : // local ViewBox which is implied by (4.2 Basic data types):
316 :
317 : // "Note that the non-property <length> definition also allows a percentage unit identifier.
318 : // The meaning of a percentage length value depends on the attribute for which the percentage
319 : // length value has been specified. Two common cases are: (a) when a percentage length value
320 : // represents a percentage of the viewport width or height (refer to the section that discusses
321 : // units in general), and (b) when a percentage length value represents a percentage of the
322 : // bounding box width or height on a given object (refer to the section that describes object
323 : // bounding box units)."
324 :
325 : // Comparisons with commom browsers show, that it's mostly interpreted relative to the viewport
326 : // of the parent, and so does the new implementation.
327 :
328 : // Extract known viewport data
329 : // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
330 :
331 : // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
332 : // value 0.0 here is only to initialize variable
333 0 : bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
334 0 : double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
335 :
336 0 : bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
337 0 : double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
338 :
339 : // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
340 0 : bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
341 0 : double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
342 :
343 0 : bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
344 0 : double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
345 :
346 0 : if ( !bXIsAbsolute || !bWidthIsAbsolute)
347 : {
348 : // get width of enclosing svg and resolve percentage in x and width;
349 0 : double fWReference(0.0);
350 0 : bool bHasFoundWidth(false);
351 0 : seekReferenceWidth(fWReference, bHasFoundWidth);
352 0 : if (!bHasFoundWidth)
353 : {
354 : // Even outermost svg has not all information to resolve relative values,
355 : // I use content itself as fallback to set missing values for viewport
356 : // Any better idea for such ill structures svg documents?
357 : const basegfx::B2DRange aChildRange(
358 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
359 : aSequence,
360 0 : drawinglayer::geometry::ViewInformation2D()));
361 0 : fWReference = aChildRange.getWidth();
362 : }
363 : // referenced values are already in 'user unit'
364 0 : if (!bXIsAbsolute)
365 : {
366 0 : fX = getX().getNumber() * 0.01 * fWReference;
367 : }
368 0 : if (!bWidthIsAbsolute)
369 : {
370 0 : fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
371 : }
372 : }
373 :
374 0 : if ( !bYIsAbsolute || !bHeightIsAbsolute)
375 : {
376 : // get height of enclosing svg and resolve percentage in y and height
377 0 : double fHReference(0.0);
378 0 : bool bHasFoundHeight(false);
379 0 : seekReferenceHeight(fHReference, bHasFoundHeight);
380 0 : if (!bHasFoundHeight)
381 : {
382 : // Even outermost svg has not all information to resolve relative values,
383 : // I use content itself as fallback to set missing values for viewport
384 : // Any better idea for such ill structures svg documents?
385 : const basegfx::B2DRange aChildRange(
386 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
387 : aSequence,
388 0 : drawinglayer::geometry::ViewInformation2D()));
389 0 : fHReference = aChildRange.getHeight();
390 : }
391 :
392 : // referenced values are already in 'user unit'
393 0 : if (!bYIsAbsolute)
394 : {
395 0 : fY = getY().getNumber() * 0.01 * fHReference;
396 : }
397 0 : if (!bHeightIsAbsolute)
398 : {
399 0 : fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
400 : }
401 : }
402 :
403 0 : if(getViewBox())
404 : {
405 : // SVG 1.1 defines in section 7.7 that a negative value for width or height
406 : // in viewBox is an error and that 0.0 disables rendering
407 0 : if(basegfx::fTools::more(getViewBox()->getWidth(),0.0) && basegfx::fTools::more(getViewBox()->getHeight(),0.0))
408 : {
409 : // create target range homing x,y, width and height as calculated above
410 0 : const basegfx::B2DRange aTarget(fX, fY, fX + fW, fY + fH);
411 :
412 0 : if(aTarget.equal(*getViewBox()))
413 : {
414 : // no mapping needed, append
415 0 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
416 : }
417 : else
418 : {
419 : // create mapping
420 : // #i122610 SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
421 : // then the effect is as if a value of 'xMidYMid meet' were specified.
422 0 : SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
423 0 : const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
424 :
425 : // let mapping be created from SvgAspectRatio
426 : const basegfx::B2DHomMatrix aEmbeddingTransform(
427 0 : rRatio.createMapping(aTarget, *getViewBox()));
428 :
429 : // prepare embedding in transformation
430 : const drawinglayer::primitive2d::Primitive2DReference xRef(
431 : new drawinglayer::primitive2d::TransformPrimitive2D(
432 : aEmbeddingTransform,
433 0 : aSequence));
434 :
435 0 : if(rRatio.isMeetOrSlice())
436 : {
437 : // embed in transformation
438 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
439 : }
440 : else
441 : {
442 : // need to embed in MaskPrimitive2D, too
443 : const drawinglayer::primitive2d::Primitive2DReference xMask(
444 : new drawinglayer::primitive2d::MaskPrimitive2D(
445 : basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aTarget)),
446 0 : drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1)));
447 :
448 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
449 0 : }
450 : }
451 : }
452 : }
453 : else // no viewBox attribute
454 : {
455 : // Svg defines that a negative value is an error and that 0.0 disables rendering
456 0 : if(basegfx::fTools::more(fW, 0.0) && basegfx::fTools::more(fH, 0.0))
457 : {
458 0 : if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
459 : {
460 : // embed in transform
461 : const drawinglayer::primitive2d::Primitive2DReference xRef(
462 : new drawinglayer::primitive2d::TransformPrimitive2D(
463 : basegfx::tools::createTranslateB2DHomMatrix(fX, fY),
464 0 : aSequence));
465 :
466 0 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
467 : }
468 :
469 : // embed in MaskPrimitive2D to clip
470 : const drawinglayer::primitive2d::Primitive2DReference xMask(
471 : new drawinglayer::primitive2d::MaskPrimitive2D(
472 : basegfx::B2DPolyPolygon(
473 : basegfx::tools::createPolygonFromRect(
474 : basegfx::B2DRange(fX, fY, fX + fW, fY + fH))),
475 0 : aSequence));
476 :
477 : // append
478 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
479 : }
480 : }
481 : }
482 : else // Outermost SVG element
483 : {
484 299 : double fW = 0.0; // effective value depends on viewBox
485 299 : double fH = 0.0;
486 :
487 : // Svg defines that a negative value is an error and that 0.0 disables rendering
488 : // isPositive() not usable because it allows 0.0 in contrast to mathematical definition of 'positive'
489 299 : const bool bWidthInvalid(getWidth().isSet() && basegfx::fTools::lessOrEqual(getWidth().getNumber(), 0.0));
490 299 : const bool bHeightInvalid(getHeight().isSet() && basegfx::fTools::lessOrEqual(getHeight().getNumber(), 0.0));
491 299 : if(!bWidthInvalid && !bHeightInvalid)
492 : {
493 299 : basegfx::B2DRange aSvgCanvasRange; // effective value depends on viewBox
494 299 : if(getViewBox())
495 : {
496 : // SVG 1.1 defines in section 7.7 that a negative value for width or height
497 : // in viewBox is an error and that 0.0 disables rendering
498 295 : const double fViewBoxWidth = getViewBox()->getWidth();
499 295 : const double fViewBoxHeight = getViewBox()->getHeight();
500 295 : if(basegfx::fTools::more(fViewBoxWidth,0.0) && basegfx::fTools::more(fViewBoxHeight,0.0))
501 : {
502 : // The intrinsic aspect ratio of the svg element is given by absolute values of both width and height
503 : // or if one or both of them is relative by the width and height of the viewBox
504 : // see SVG 1.1 section 7.12
505 295 : const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
506 295 : const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
507 295 : if(bWidthIsAbsolute && bHeightIsAbsolute)
508 : {
509 295 : fW =getWidth().solveNonPercentage(*this);
510 295 : fH =getHeight().solveNonPercentage(*this);
511 : }
512 0 : else if (bWidthIsAbsolute)
513 : {
514 0 : fW = getWidth().solveNonPercentage(*this);
515 0 : fH = fW * fViewBoxWidth / fViewBoxHeight ;
516 : }
517 0 : else if (bHeightIsAbsolute)
518 : {
519 0 : fH = getHeight().solveNonPercentage(*this);
520 0 : fW = fH * fViewBoxWidth / fViewBoxHeight ;
521 : }
522 : else
523 : {
524 0 : fW = fViewBoxWidth;
525 0 : fH = fViewBoxHeight;
526 : }
527 : // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
528 295 : aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
529 :
530 : // create mapping
531 : // SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
532 : // then the effect is as if a value of 'xMidYMid meet' were specified.
533 295 : SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
534 295 : const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
535 :
536 295 : basegfx::B2DHomMatrix aViewBoxMapping;
537 295 : aViewBoxMapping = rRatio.createMapping(aSvgCanvasRange, *getViewBox());
538 : // no need to check ratio here for slice, the outermost Svg will
539 : // be clipped anyways (see below)
540 :
541 : // scale content to viewBox definitions
542 : const drawinglayer::primitive2d::Primitive2DReference xTransform(
543 : new drawinglayer::primitive2d::TransformPrimitive2D(
544 : aViewBoxMapping,
545 590 : aSequence));
546 :
547 590 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
548 : }
549 : }
550 : else // no viewbox
551 : {
552 : // There exists no parent to resolve relative width or height.
553 : // Use child size as fallback.
554 4 : const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
555 4 : const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
556 4 : if (bWidthIsAbsolute && bHeightIsAbsolute)
557 : {
558 4 : fW =getWidth().solveNonPercentage(*this);
559 4 : fH =getHeight().solveNonPercentage(*this);
560 :
561 : }
562 : else
563 : {
564 : const basegfx::B2DRange aChildRange(
565 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
566 : aSequence,
567 0 : drawinglayer::geometry::ViewInformation2D()));
568 0 : const double fChildWidth(aChildRange.getWidth());
569 0 : const double fChildHeight(aChildRange.getHeight());
570 0 : fW = bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : fChildWidth;
571 0 : fH = bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : fChildHeight;
572 : }
573 : // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
574 4 : aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
575 : }
576 :
577 : // to be completely correct in Svg sense it is necessary to clip
578 : // the whole content to the given canvas. I choose here to do this
579 : // initially despite I found various examples of Svg files out there
580 : // which have no correct values for this clipping. It's correct
581 : // due to the Svg spec.
582 299 : bool bDoCorrectCanvasClipping(true);
583 :
584 299 : if(bDoCorrectCanvasClipping)
585 : {
586 : // different from Svg we have the possibility with primitives to get
587 : // a correct bounding box for the geometry. Get it for evtl. taking action
588 : const basegfx::B2DRange aContentRange(
589 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
590 : aSequence,
591 299 : drawinglayer::geometry::ViewInformation2D()));
592 :
593 299 : if(aSvgCanvasRange.isInside(aContentRange))
594 : {
595 : // no clip needed, but an invisible HiddenGeometryPrimitive2D
596 : // to allow getting the full Svg range using the primitive mechanisms.
597 : // This is needed since e.g. an SdrObject using this as graphic will
598 : // create a mapping transformation to exactly map the content to it's
599 : // real life size
600 : const drawinglayer::primitive2d::Primitive2DReference xLine(
601 : new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
602 : basegfx::tools::createPolygonFromRect(
603 : aSvgCanvasRange),
604 217 : basegfx::BColor(0.0, 0.0, 0.0)));
605 : const drawinglayer::primitive2d::Primitive2DReference xHidden(
606 : new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(
607 434 : drawinglayer::primitive2d::Primitive2DSequence(&xLine, 1)));
608 :
609 434 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aSequence, xHidden);
610 : }
611 82 : else if(aSvgCanvasRange.overlaps(aContentRange))
612 : {
613 : // Clip is necessary. This will make Svg images evtl. smaller
614 : // than wanted from Svg (the free space which may be around it is
615 : // conform to the Svg spec), but avoids an expensive and unnecessary
616 : // clip. Keep the full Svg range here to get the correct mappings
617 : // to objects using this. Optimizations can be done in the processors
618 : const drawinglayer::primitive2d::Primitive2DReference xMask(
619 : new drawinglayer::primitive2d::MaskPrimitive2D(
620 : basegfx::B2DPolyPolygon(
621 : basegfx::tools::createPolygonFromRect(
622 : aSvgCanvasRange)),
623 82 : aSequence));
624 :
625 82 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
626 : }
627 : else
628 : {
629 : // not inside, no overlap. Empty Svg
630 0 : aSequence.realloc(0);
631 : }
632 : }
633 :
634 299 : if(aSequence.hasElements())
635 : {
636 : // embed in transform primitive to scale to 1/100th mm
637 : // where 1 inch == 25.4 mm to get from Svg coordinates (px) to
638 : // drawinglayer coordinates
639 299 : const double fScaleTo100thmm(25.4 * 100.0 / F_SVG_PIXEL_PER_INCH);
640 : const basegfx::B2DHomMatrix aTransform(
641 : basegfx::tools::createScaleB2DHomMatrix(
642 : fScaleTo100thmm,
643 299 : fScaleTo100thmm));
644 :
645 : const drawinglayer::primitive2d::Primitive2DReference xTransform(
646 : new drawinglayer::primitive2d::TransformPrimitive2D(
647 : aTransform,
648 598 : aSequence));
649 :
650 299 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
651 :
652 : // append to result
653 598 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
654 : }
655 : }
656 : }
657 299 : }
658 299 : }
659 :
660 0 : const basegfx::B2DRange SvgSvgNode::getCurrentViewPort() const
661 : {
662 0 : if(getViewBox())
663 : {
664 0 : return *(getViewBox());
665 : }
666 : else // viewport should be given by x, y, width, and height
667 : {
668 : // Extract known viewport data
669 : // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
670 0 : if (getParent())
671 : {
672 : // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
673 : // value 0.0 here is only to initialize variable
674 0 : bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
675 0 : double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
676 0 : bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
677 0 : double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
678 :
679 : // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
680 0 : bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
681 0 : double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
682 :
683 0 : bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
684 0 : double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
685 :
686 0 : if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
687 : {
688 0 : return basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
689 : }
690 : else // try to resolve relative values
691 : {
692 0 : if (!bXIsAbsolute || !bWidthIsAbsolute)
693 : {
694 : // get width of enclosing svg and resolve percentage in x and width
695 0 : double fWReference(0.0);
696 0 : bool bHasFoundWidth(false);
697 0 : seekReferenceWidth(fWReference, bHasFoundWidth);
698 : // referenced values are already in 'user unit'
699 0 : if (!bXIsAbsolute && bHasFoundWidth)
700 : {
701 0 : fX = getX().getNumber() * 0.01 * fWReference;
702 0 : bXIsAbsolute = true;
703 : }
704 0 : if (!bWidthIsAbsolute && bHasFoundWidth)
705 : {
706 0 : fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
707 0 : bWidthIsAbsolute = true;
708 : }
709 : }
710 0 : if (!bYIsAbsolute || !bHeightIsAbsolute)
711 : {
712 : // get height of enclosing svg and resolve percentage in y and height
713 0 : double fHReference(0.0);
714 0 : bool bHasFoundHeight(false);
715 0 : seekReferenceHeight(fHReference, bHasFoundHeight);
716 : // referenced values are already in 'user unit'
717 0 : if (!bYIsAbsolute && bHasFoundHeight)
718 : {
719 0 : fY = getY().getNumber() * 0.01 * fHReference;
720 0 : bYIsAbsolute = true;
721 : }
722 0 : if (!bHeightIsAbsolute && bHasFoundHeight)
723 : {
724 0 : fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
725 0 : bHeightIsAbsolute = true;
726 : }
727 : }
728 :
729 0 : if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
730 : {
731 0 : return basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
732 : }
733 : else // relative values could not be resolved, there exists no fallback
734 : {
735 0 : return SvgNode::getCurrentViewPort();
736 : }
737 : }
738 : }
739 : else //outermost svg
740 : {
741 : // If width or height is not provided, the default would be 100%, see SVG 1.1 section 5.1.2
742 : // But here it cannot be resolved and no fallback exists.
743 : // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
744 0 : bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
745 0 : double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
746 0 : bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
747 0 : double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
748 0 : if (bWidthIsAbsolute && bHeightIsAbsolute)
749 : {
750 0 : return basegfx::B2DRange(0.0, 0.0, fW, fH);
751 : }
752 : else // no fallback exists
753 : {
754 0 : return SvgNode::getCurrentViewPort();
755 : }
756 : }
757 : // ToDo: Is it possible to decompose and use the bounding box of the children, if even the
758 : // outermost svg has no information to resolve percentage? Is it worth, how expensive is it?
759 :
760 : }
761 : }
762 :
763 : } // end of namespace svgreader
764 : } // end of namespace svgio
765 :
766 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|