1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include "svgreader.hxx"
11 #include <xmloff/attrlist.hxx>
12 #include "gfxtypes.hxx"
14 #include "parserfragments.hxx"
15 #include "tokenmap.hxx"
16 #include "b2dellipse.hxx"
18 #include <rtl/math.hxx>
19 #include <rtl/ref.hxx>
20 #include <rtl/ustring.hxx>
21 #include <rtl/ustrbuf.hxx>
22 #include <basegfx/vector/b2enums.hxx>
23 #include <basegfx/range/b2drange.hxx>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/polygon/b2dpolypolygon.hxx>
26 #include <basegfx/polygon/b2dlinegeometry.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/polygon/b2dpolypolygontools.hxx>
29 #include <com/sun/star/io/XSeekable.hpp>
30 #include <com/sun/star/xml/sax/XParser.hpp>
31 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
32 #include <com/sun/star/xml/dom/NodeType.hpp>
34 #include <comphelper/processfactory.hxx>
35 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
36 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
37 #include <unotools/streamwrap.hxx>
38 #include <sax/tools/converter.hxx>
39 #include <vcl/graph.hxx>
40 #include <vcl/virdev.hxx>
41 #include <vcl/gradient.hxx>
42 #include <vcl/graphicfilter.hxx>
43 #include <tools/zcodec.hxx>
47 #define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"
49 using namespace ::com::sun::star
;
53 enum SvgiVisitorCaller
{STYLE_ANNOTATOR
, SHAPE_WRITER
, STYLE_WRITER
};
57 /** visits all children of the specified type with the given functor
59 template<typename Func
> void visitChildren(const Func
& rFunc
,
60 const uno::Reference
<xml::dom::XElement
>& rElem
,
61 xml::dom::NodeType eChildType
)
63 uno::Reference
<xml::dom::XNodeList
> xChildren( rElem
->getChildNodes() );
64 const sal_Int32
nNumNodes( xChildren
->getLength() );
65 for( sal_Int32 i
=0; i
<nNumNodes
; ++i
)
67 SAL_INFO("svg", "node type: " << sal::static_int_cast
<sal_uInt32
>(xChildren
->item(i
)->getNodeType()) << " tag name " << xChildren
->item(i
)->getNodeName() << " value |" << xChildren
->item(i
)->getNodeValue() << "|");
68 if( xChildren
->item(i
)->getNodeType() == eChildType
)
69 rFunc( *xChildren
->item(i
).get() );
73 /** Visit all elements of the given tree (in-order traversal)
75 Given functor is called for every element, and passed the
76 element's attributes, if any
78 template<typename Func
> void visitElements(Func
& rFunc
,
79 const uno::Reference
<xml::dom::XElement
>& rElem
,
80 SvgiVisitorCaller eCaller
)
82 if( rElem
->hasAttributes() )
83 rFunc(rElem
, rElem
->getAttributes());
87 // notify children processing
90 if (eCaller
== SHAPE_WRITER
&& rElem
->getTagName() == "defs")
93 // recurse over children
94 uno::Reference
<xml::dom::XNodeList
> xChildren( rElem
->getChildNodes() );
95 const sal_Int32
nNumNodes( xChildren
->getLength() );
96 for( sal_Int32 i
=0; i
<nNumNodes
; ++i
)
98 if( xChildren
->item(i
)->getNodeType() == xml::dom::NodeType_ELEMENT_NODE
){
100 uno::Reference
<xml::dom::XElement
>(
102 uno::UNO_QUERY_THROW
),
107 // children processing done
111 template<typename value_type
> value_type
square(value_type v
)
116 double colorDiffSquared(const ARGBColor
& rCol1
, const ARGBColor
& rCol2
)
119 square(rCol1
.a
-rCol2
.a
)
120 + square(rCol1
.r
-rCol2
.r
)
121 + square(rCol1
.g
-rCol2
.g
)
122 + square(rCol1
.b
-rCol2
.b
);
126 check whether a polypolygon contains both open and closed
129 bool PolyPolygonIsMixedOpenAndClosed( const basegfx::B2DPolyPolygon
& rPoly
)
135 // PolyPolygon is mixed open and closed if there is more than one
136 // polygon and there are both closed and open polygons.
137 for( sal_uInt32
a(0L); !bRetval
&& a
< rPoly
.count(); a
++ )
139 if ( (rPoly
.getB2DPolygon(a
)).isClosed() )
148 bRetval
= (bClosed
&& bOpen
);
154 typedef std::map
<OUString
,std::size_t> ElementRefMapType
;
156 struct AnnotatingVisitor
158 AnnotatingVisitor(StatePool
& rStatePool
,
160 const State
& rInitialState
,
161 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocumentHandler
,
162 std::vector
< uno::Reference
<xml::dom::XElement
> >& rUseElementVector
,
163 bool& rGradientNotFound
) :
167 mrStates(rStatePool
),
168 mrStateMap(rStateMap
),
169 mxDocumentHandler(xDocumentHandler
),
171 maGradientStopVector(),
173 mrUseElementVector(rUseElementVector
),
174 mrGradientNotFound(rGradientNotFound
)
176 maParentStates
.push_back(rInitialState
);
179 void operator()( const uno::Reference
<xml::dom::XElement
>& xElem
)
181 const sal_Int32
nTagId(getTokenId(xElem
->getTagName()));
182 if (nTagId
!= XML_TEXT
&& nTagId
!= XML_TSPAN
)
185 maCurrState
= maParentStates
.back();
186 maCurrState
.maTransform
.identity();
187 maCurrState
.maViewBox
.reset();
188 // if necessary, serialize to automatic-style section
189 writeStyle(xElem
,nTagId
);
192 void operator()( const uno::Reference
<xml::dom::XElement
>& xElem
,
193 const uno::Reference
<xml::dom::XNamedNodeMap
>& xAttributes
)
195 const sal_Int32
nTagId(getTokenId(xElem
->getTagName()));
198 case XML_LINEARGRADIENT
:
200 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
201 maGradientVector
.push_back(Gradient(Gradient::LINEAR
));
203 // do we have a reference to a parent gradient? parse
204 // that first, as it sets our defaults here (manually
205 // tracking default state on each Gradient variable is
206 // much more overhead)
207 uno::Reference
<xml::dom::XNode
> xNode(xAttributes
->getNamedItem("href"));
210 const OUString
sValue(xNode
->getNodeValue());
211 ElementRefMapType::iterator aFound
=maGradientIdMap
.end();
212 if ( sValue
.copy(0,1) == "#" )
213 aFound
= maGradientIdMap
.find(sValue
.copy(1));
215 aFound
= maGradientIdMap
.find(sValue
);
217 if( aFound
!= maGradientIdMap
.end() )
218 maGradientVector
.back() = maGradientVector
[aFound
->second
];
221 mrGradientNotFound
= true;
222 maGradientVector
.pop_back();
227 // do that after dereferencing, to prevent hyperlinked
228 // gradient to clobber our Id again
229 maGradientVector
.back().mnId
= maGradientVector
.size()-1;
230 maGradientVector
.back().meType
= Gradient::LINEAR
; // has been clobbered as well
232 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
234 parseLinearGradientData( maGradientVector
.back(),
235 maGradientVector
.size()-1,
236 getTokenId(xAttributes
->item(i
)->getNodeName()),
237 xAttributes
->item(i
)->getNodeValue() );
241 case XML_RADIALGRADIENT
:
243 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
244 maGradientVector
.push_back(Gradient(Gradient::RADIAL
));
246 // do we have a reference to a parent gradient? parse
247 // that first, as it sets our defaults here (manually
248 // tracking default state on each Gradient variable is
249 // much more overhead)
250 uno::Reference
<xml::dom::XNode
> xNode(xAttributes
->getNamedItem("href"));
253 const OUString
sValue(xNode
->getNodeValue());
254 ElementRefMapType::iterator aFound
=maGradientIdMap
.end();
255 if ( sValue
.copy(0,1) == "#" )
256 aFound
= maGradientIdMap
.find(sValue
.copy(1));
258 aFound
= maGradientIdMap
.find(sValue
);
260 if( aFound
!= maGradientIdMap
.end() )
261 maGradientVector
.back() = maGradientVector
[aFound
->second
];
264 mrGradientNotFound
= true;
265 maGradientVector
.pop_back();
270 // do that after dereferencing, to prevent hyperlinked
271 // gradient to clobber our Id again
272 maGradientVector
.back().mnId
= maGradientVector
.size()-1;
273 maGradientVector
.back().meType
= Gradient::RADIAL
; // has been clobbered as well
275 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
277 parseRadialGradientData( maGradientVector
.back(),
278 maGradientVector
.size()-1,
279 getTokenId(xAttributes
->item(i
)->getNodeName()),
280 xAttributes
->item(i
)->getNodeValue() );
286 uno::Reference
<xml::dom::XNode
> xNode(xAttributes
->getNamedItem("href"));
289 OUString
sValue(xNode
->getNodeValue());
290 ElementRefMapType::iterator aFound
=maElementIdMap
.end();
291 if ( sValue
.copy(0,1) == "#" )
292 sValue
= sValue
.copy(1);
293 aFound
= maElementIdMap
.find(sValue
);
294 bool bFound
= aFound
!= maElementIdMap
.end();
297 bool bSelfCycle
= false;
299 uno::Reference
<xml::dom::XNode
> xParentNode(xElem
->getParentNode());
300 if (xParentNode
.is() && xParentNode
->hasAttributes())
302 const uno::Reference
<xml::dom::XNamedNodeMap
> xParentAttributes
= xParentNode
->getAttributes();
303 const sal_Int32
nFooNumAttrs(xParentAttributes
->getLength());
304 for (sal_Int32 i
=0; i
< nFooNumAttrs
; ++i
)
306 const sal_Int32
nTokenId(getTokenId(xParentAttributes
->item(i
)->getNodeName()));
307 if (XML_ID
== nTokenId
)
309 OUString sParentID
= xParentAttributes
->item(i
)->getNodeValue();
310 bSelfCycle
= sParentID
== sValue
;
318 //drop this invalid self-referencing "use" node
319 maElementIdMap
.erase(aFound
);
326 uno::Reference
<xml::dom::XElement
> xRefElem(
327 maElementVector
[aFound
->second
]->cloneNode(true), uno::UNO_QUERY
);
329 xRefElem
->removeAttribute("id");
330 uno::Reference
<xml::dom::XNode
> xAttrNode
;
332 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
333 OUString sAttributeValue
;
335 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
337 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
338 const sal_Int32
nAttribId(
339 getTokenId(xAttributes
->item(i
)->getNodeName()));
344 maElementVector
.push_back(xElem
);
345 maElementIdMap
.insert(std::make_pair(sAttributeValue
,
346 maElementVector
.size() - 1));
349 x
= convLength(sAttributeValue
,maCurrState
,'h');
352 y
= convLength(sAttributeValue
,maCurrState
,'v');
357 OUString sAttributeName
= xAttributes
->item(i
)->getNodeName();
358 xRefElem
->setAttribute(sAttributeName
, sAttributeValue
);
362 std::stringstream ssAttrValue
;
363 ssAttrValue
<< xRefElem
->getAttribute("transform");
364 ssAttrValue
<< xElem
->getAttribute("transform");
365 ssAttrValue
<< " translate(" << x
<< "," << y
<< ")";
367 OUString
attrValue(OUString::createFromAscii (ssAttrValue
.str().c_str()));
368 xRefElem
->setAttribute("transform", attrValue
);
370 mrUseElementVector
.push_back(xRefElem
);
372 visitElements((*this), xRefElem
, STYLE_ANNOTATOR
);
379 if (!maGradientVector
.empty())
381 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
382 maGradientStopVector
.push_back(GradientStop());
383 maGradientVector
.back().maStops
.push_back(maGradientStopVector
.size()-1);
385 // first parse 'color' as 'stop-color' might depend on it
386 // if 'stop-color''s value is "currentColor" and parsed previously
387 uno::Reference
<xml::dom::XNode
> xNodeColor(xAttributes
->getNamedItem("color"));
389 parseGradientStop( maGradientStopVector
.back(),
390 maGradientStopVector
.size()-1,
392 xNodeColor
->getNodeValue() );
394 //now, parse the rest of attributes
395 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
397 const sal_Int32
nTokenId(
398 getTokenId(xAttributes
->item(i
)->getNodeName()));
399 if ( nTokenId
!= XML_COLOR
)
400 parseGradientStop( maGradientStopVector
.back(),
401 maGradientStopVector
.size()-1,
403 xAttributes
->item(i
)->getNodeValue() );
410 if ( !mrGradientNotFound
)
412 // init state. inherit defaults from parent.
413 maCurrState
= maParentStates
.back();
414 maCurrState
.maTransform
.identity();
415 maCurrState
.maViewBox
.reset();
417 // first parse 'color' and 'style' as 'fill' and 'stroke' might depend on them
418 // if their values are "currentColor" and parsed previously
419 uno::Reference
<xml::dom::XNode
> xNodeColor(xAttributes
->getNamedItem("color"));
421 parseAttribute(XML_COLOR
, xNodeColor
->getNodeValue());
423 uno::Reference
<xml::dom::XNode
> xNodeStyle(xAttributes
->getNamedItem("style"));
425 parseStyle(xNodeStyle
->getNodeValue());
427 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
428 OUString sAttributeValue
;
430 //now, parse the rest of attributes
431 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
433 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
434 const sal_Int32
nTokenId(
435 getTokenId(xAttributes
->item(i
)->getNodeName()));
436 if( XML_ID
== nTokenId
)
438 maElementVector
.push_back(xElem
);
439 maElementIdMap
.insert(std::make_pair(sAttributeValue
,
440 maElementVector
.size() - 1));
442 else if (nTokenId
!= XML_COLOR
&& nTokenId
!= XML_STYLE
)
443 parseAttribute(nTokenId
,
447 // all attributes parsed, can calc total CTM now
448 basegfx::B2DHomMatrix aLocalTransform
;
449 if( !maCurrState
.maViewBox
.isEmpty() &&
450 maCurrState
.maViewBox
.getWidth() != 0.0 &&
451 maCurrState
.maViewBox
.getHeight() != 0.0 )
453 // transform aViewBox into viewport, keep aspect ratio
454 aLocalTransform
.translate(-maCurrState
.maViewBox
.getMinX(),
455 -maCurrState
.maViewBox
.getMinY());
456 double scaleW
= maCurrState
.maViewport
.getWidth()/maCurrState
.maViewBox
.getWidth();
457 double scaleH
= maCurrState
.maViewport
.getHeight()/maCurrState
.maViewBox
.getHeight();
458 double scale
= (scaleW
< scaleH
) ? scaleW
: scaleH
;
459 aLocalTransform
.scale(scale
,scale
);
461 else if( !maParentStates
.back().maViewBox
.isEmpty() )
462 maCurrState
.maViewBox
= maParentStates
.back().maViewBox
;
464 maCurrState
.maCTM
= maCurrState
.maCTM
*maCurrState
.maTransform
*aLocalTransform
;
466 OSL_TRACE("annotateStyle - CTM is: %f %f %f %f %f %f",
467 maCurrState
.maCTM
.get(0,0),
468 maCurrState
.maCTM
.get(0,1),
469 maCurrState
.maCTM
.get(0,2),
470 maCurrState
.maCTM
.get(1,0),
471 maCurrState
.maCTM
.get(1,1),
472 maCurrState
.maCTM
.get(1,2));
474 // if necessary, serialize to automatic-style section
475 writeStyle(xElem
,nTagId
);
481 static OUString
getStyleName( const char* sPrefix
, sal_Int32 nId
)
483 return OUString::createFromAscii(sPrefix
)+OUString::number(nId
);
486 bool hasGradientOpacity( const Gradient
& rGradient
)
489 (rGradient
.maStops
.size() > 1) &&
490 (maGradientStopVector
[
491 rGradient
.maStops
[0]].maStopColor
.a
!= 1.0 ||
492 maGradientStopVector
[
493 rGradient
.maStops
[1]].maStopColor
.a
!= 1.0);
498 explicit StopSorter( const std::vector
< GradientStop
>& rStopVec
) :
502 bool operator()( std::size_t rLHS
, std::size_t rRHS
)
504 return mrStopVec
[rLHS
].mnStopPosition
< mrStopVec
[rRHS
].mnStopPosition
;
507 const std::vector
< GradientStop
>& mrStopVec
;
510 void optimizeGradientStops( Gradient
& rGradient
)
512 // sort for increasing stop position
513 std::sort(rGradient
.maStops
.begin(),rGradient
.maStops
.end(),
514 StopSorter(maGradientStopVector
));
516 if( rGradient
.maStops
.size() < 3 )
519 // join similar colors
520 std::vector
<std::size_t> aNewStops
{ rGradient
.maStops
.front() };
521 for( std::size_t i
=1; i
<rGradient
.maStops
.size(); ++i
)
523 if( maGradientStopVector
[rGradient
.maStops
[i
]].maStopColor
!=
524 maGradientStopVector
[aNewStops
.back()].maStopColor
)
525 aNewStops
.push_back(rGradient
.maStops
[i
]);
528 rGradient
.maStops
= aNewStops
;
529 if (rGradient
.maStops
.size() < 2)
531 return; // can't optimize further...
534 // axial gradient, maybe?
535 if( rGradient
.meType
== Gradient::LINEAR
&&
536 rGradient
.maStops
.size() == 3 &&
537 maGradientStopVector
[rGradient
.maStops
.front()].maStopColor
==
538 maGradientStopVector
[rGradient
.maStops
.back()].maStopColor
)
540 // yep - keep it at that
544 // find out most significant color difference, and limit to
545 // those two stops around this border (metric is
546 // super-simplistic: take euclidean distance of colors, weigh
547 // with stop distance)
548 std::size_t nMaxIndex
=0;
549 double fMaxDistance
=0.0;
550 for( std::size_t i
=1; i
<rGradient
.maStops
.size(); ++i
)
552 const double fCurrDistance(
554 maGradientStopVector
[rGradient
.maStops
[i
-1]].maStopColor
,
555 maGradientStopVector
[rGradient
.maStops
[i
]].maStopColor
) *
556 (square(maGradientStopVector
[rGradient
.maStops
[i
-1]].mnStopPosition
) +
557 square(maGradientStopVector
[rGradient
.maStops
[i
]].mnStopPosition
)) );
559 if( fCurrDistance
> fMaxDistance
)
562 fMaxDistance
= fCurrDistance
;
565 rGradient
.maStops
[0] = rGradient
.maStops
[nMaxIndex
];
566 rGradient
.maStops
[1] = rGradient
.maStops
[nMaxIndex
+1];
567 rGradient
.maStops
.erase(rGradient
.maStops
.begin()+2,rGradient
.maStops
.end());
570 static sal_Int8
toByteColor( double val
)
572 // TODO(Q3): duplicated from vcl::unotools
573 return sal::static_int_cast
<sal_Int8
>(
574 basegfx::fround(val
*255.0));
577 static OUString
getOdfColor( const ARGBColor
& rColor
)
579 // TODO(Q3): duplicated from pdfimport
580 OUStringBuffer
aBuf( 7 );
581 const sal_uInt8
nRed ( toByteColor(rColor
.r
) );
582 const sal_uInt8
nGreen( toByteColor(rColor
.g
) );
583 const sal_uInt8
nBlue ( toByteColor(rColor
.b
) );
587 aBuf
.append( sal_Int32(nRed
), 16 );
590 aBuf
.append( sal_Int32(nGreen
), 16 );
593 aBuf
.append( sal_Int32(nBlue
), 16 );
595 // TODO(F3): respect alpha transparency (polygons etc.)
596 OSL_ASSERT(rColor
.a
== 1.0);
598 return aBuf
.makeStringAndClear();
601 static OUString
getOdfAlign( TextAlign eAlign
)
607 return OUString("start");
609 return OUString("center");
611 return OUString("end");
615 bool writeStyle(State
& rState
, const sal_Int32 nTagId
)
617 rtl::Reference
<SvXMLAttributeList
> xAttrs( new SvXMLAttributeList() );
618 uno::Reference
<xml::sax::XAttributeList
> xUnoAttrs( xAttrs
.get() );
620 if (XML_TEXT
== nTagId
|| XML_TSPAN
== nTagId
) {
621 rState
.mbIsText
= true;
622 basegfx::B2DTuple aScale
, aTranslate
;
623 double fRotate
, fShearX
;
624 if (rState
.maCTM
.decompose(aScale
, aTranslate
, fRotate
, fShearX
))
626 rState
.mnFontSize
*= aScale
.getX();
630 std::pair
<StatePool::iterator
,
631 bool> aRes
= mrStates
.insert(rState
);
632 SAL_INFO ("svg", "size " << mrStates
.size() << " id " << const_cast<State
&>(*aRes
.first
).mnStyleId
);
635 return false; // not written
639 // mnStyleId does not take part in hashing/comparison
640 const_cast<State
&>(*aRes
.first
).mnStyleId
= mnCurrStateId
;
641 SAL_INFO ("svg", " --> " << const_cast<State
&>(*aRes
.first
).mnStyleId
);
643 mrStateMap
.insert(std::make_pair(
647 // find two representative stop colors (as ODF only support
649 optimizeGradientStops(rState
.maFillGradient
);
651 if( !mxDocumentHandler
.is() )
652 return true; // cannot write style, svm import case
654 // do we have a gradient fill? then write out gradient as well
655 if( rState
.meFillType
== GRADIENT
&& rState
.maFillGradient
.maStops
.size() > 0 )
657 // if only one stop-color is defined
658 if( rState
.maFillGradient
.maStops
.size() == 1 )
659 rState
.maFillGradient
.maStops
.push_back(rState
.maFillGradient
.maStops
[0]);
661 // TODO(F3): ODF12 supposedly also groks svg:linear/radialGradient. But CL says: nope.
662 xAttrs
->AddAttribute( "draw:name", getStyleName("svggradient", rState
.maFillGradient
.mnId
) );
663 if( rState
.maFillGradient
.meType
== Gradient::LINEAR
)
665 // should the optimizeGradientStops method decide that
666 // this is a three-color gradient, it prolly wanted us
667 // to take axial instead
668 xAttrs
->AddAttribute( "draw:style",
669 rState
.maFillGradient
.maStops
.size() == 3 ?
671 OUString("linear") );
675 xAttrs
->AddAttribute( "draw:style", "ellipsoid" );
676 xAttrs
->AddAttribute( "draw:cx", "50%" );
677 xAttrs
->AddAttribute( "draw:cy", "50%" );
680 basegfx::B2DTuple rScale
, rTranslate
;
681 double rRotate
, rShearX
;
682 if( rState
.maFillGradient
.maTransform
.decompose(rScale
, rTranslate
, rRotate
, rShearX
) )
683 xAttrs
->AddAttribute( "draw:angle",
684 OUString::number(rRotate
*1800.0/M_PI
) );
685 xAttrs
->AddAttribute( "draw:start-color",
687 maGradientStopVector
[
688 rState
.maFillGradient
.maStops
[0]].maStopColor
) );
689 xAttrs
->AddAttribute( "draw:end-color",
691 maGradientStopVector
[
692 rState
.maFillGradient
.maStops
[1]].maStopColor
) );
693 xAttrs
->AddAttribute( "draw:border", "0%" );
694 mxDocumentHandler
->startElement( "draw:gradient", xUnoAttrs
);
695 mxDocumentHandler
->endElement( "draw:gradient" );
697 if( hasGradientOpacity(rState
.maFillGradient
) )
699 // need to write out opacity style as well
701 xAttrs
->AddAttribute( "draw:name", getStyleName("svgopacity", rState
.maFillGradient
.mnId
) );
702 if( rState
.maFillGradient
.meType
== Gradient::LINEAR
)
704 xAttrs
->AddAttribute( "draw:style", "linear" );
708 xAttrs
->AddAttribute( "draw:style", "ellipsoid" );
709 xAttrs
->AddAttribute( "draw:cx", "50%" );
710 xAttrs
->AddAttribute( "draw:cy", "50%" );
713 // modulate gradient opacity with overall fill opacity
714 xAttrs
->AddAttribute( "draw:end",
716 maGradientStopVector
[
717 rState
.maFillGradient
.maStops
[0]].maStopColor
.a
*
718 maCurrState
.mnFillOpacity
*maCurrState
.mnOpacity
*100.0)+"%" );
719 xAttrs
->AddAttribute( "draw:start",
721 maGradientStopVector
[
722 rState
.maFillGradient
.maStops
[1]].maStopColor
.a
*
723 maCurrState
.mnFillOpacity
*maCurrState
.mnOpacity
*100.0)+"%" );
724 xAttrs
->AddAttribute( "draw:border", "0%" );
725 mxDocumentHandler
->startElement( "draw:opacity", xUnoAttrs
);
726 mxDocumentHandler
->endElement( "draw:opacity" );
730 // serialize to automatic-style section
731 if( nTagId
== XML_TEXT
|| nTagId
== XML_TSPAN
)
733 // write paragraph style attributes
736 xAttrs
->AddAttribute( "style:name", getStyleName("svgparagraphstyle", mnCurrStateId
) );
737 xAttrs
->AddAttribute( "style:family", "paragraph" );
738 mxDocumentHandler
->startElement( "style:style", xUnoAttrs
);
741 xAttrs
->AddAttribute( "fo:text-align", getOdfAlign(rState
.meTextAnchor
));
743 mxDocumentHandler
->startElement( "style:paragraph-properties", xUnoAttrs
);
744 mxDocumentHandler
->endElement( "style:paragraph-properties" );
745 mxDocumentHandler
->endElement( "style:style" );
747 // write text style attributes
750 xAttrs
->AddAttribute( "style:name", getStyleName("svgtextstyle", mnCurrStateId
) );
751 xAttrs
->AddAttribute( "style:family", "text" );
752 mxDocumentHandler
->startElement( "style:style", xUnoAttrs
);
754 xAttrs
->AddAttribute( "fo:font-family", rState
.maFontFamily
);
755 xAttrs
->AddAttribute( "fo:font-size",
756 OUString::number(pt2mm(rState
.mnFontSize
))+"mm");
757 xAttrs
->AddAttribute( "fo:font-style", rState
.maFontStyle
);
758 xAttrs
->AddAttribute( "fo:font-variant", rState
.maFontVariant
);
759 xAttrs
->AddAttribute( "fo:font-weight",
760 OUString::number(rState
.mnFontWeight
));
761 xAttrs
->AddAttribute( "fo:color", getOdfColor(rState
.maFillColor
));
763 mxDocumentHandler
->startElement( "style:text-properties", xUnoAttrs
);
764 mxDocumentHandler
->endElement( "style:text-properties" );
765 mxDocumentHandler
->endElement( "style:style" );
769 xAttrs
->AddAttribute( "style:name" , getStyleName("svggraphicstyle", mnCurrStateId
) );
770 xAttrs
->AddAttribute( "style:family", "graphic" );
771 mxDocumentHandler
->startElement( "style:style", xUnoAttrs
);
774 // text or shape? if the former, no use in processing any
775 // graphic attributes except stroke color, ODF can do ~nothing
777 if( nTagId
== XML_TEXT
|| nTagId
== XML_TSPAN
)
779 //xAttrs->AddAttribute( "draw:auto-grow-height", "true");
780 xAttrs
->AddAttribute( "draw:auto-grow-width", "true");
781 xAttrs
->AddAttribute( "draw:textarea-horizontal-align", "left");
782 //xAttrs->AddAttribute( "draw:textarea-vertical-align", "top");
783 xAttrs
->AddAttribute( "fo:min-height", "0cm");
785 xAttrs
->AddAttribute( "fo:padding-top", "0cm");
786 xAttrs
->AddAttribute( "fo:padding-left", "0cm");
787 xAttrs
->AddAttribute( "fo:padding-right", "0cm");
788 xAttrs
->AddAttribute( "fo:padding-bottom", "0cm");
790 // disable any background shape
791 xAttrs
->AddAttribute( "draw:stroke", "none");
792 xAttrs
->AddAttribute( "draw:fill", "none");
796 if( rState
.meFillType
!= NONE
)
798 if( rState
.meFillType
== GRADIENT
)
800 // don't fill the gradient if there's no stop element present
801 if( rState
.maFillGradient
.maStops
.size() == 0 )
802 xAttrs
->AddAttribute( "draw:fill", "none" );
805 xAttrs
->AddAttribute( "draw:fill", "gradient");
806 xAttrs
->AddAttribute( "draw:fill-gradient-name",
807 getStyleName("svggradient", rState
.maFillGradient
.mnId
) );
808 if( hasGradientOpacity(rState
.maFillGradient
) )
810 // needs transparency gradient as well
811 xAttrs
->AddAttribute( "draw:opacity-name",
812 getStyleName("svgopacity", rState
.maFillGradient
.mnId
) );
814 else if( maCurrState
.mnFillOpacity
*maCurrState
.mnOpacity
!= 1.0 )
815 xAttrs
->AddAttribute( "draw:opacity",
816 OUString::number(100.0*maCurrState
.mnFillOpacity
*maCurrState
.mnOpacity
)+"%" );
821 xAttrs
->AddAttribute( "draw:fill", "solid");
822 xAttrs
->AddAttribute( "draw:fill-color", getOdfColor(rState
.maFillColor
));
823 if( maCurrState
.mnFillOpacity
*maCurrState
.mnOpacity
!= 1.0 )
824 xAttrs
->AddAttribute( "draw:opacity",
825 OUString::number(100.0*maCurrState
.mnFillOpacity
*maCurrState
.mnOpacity
)+"%" );
829 xAttrs
->AddAttribute( "draw:fill", "none");
831 if( rState
.meStrokeType
== SOLID
)
833 xAttrs
->AddAttribute( "draw:stroke", "solid");
834 xAttrs
->AddAttribute( "svg:stroke-color", getOdfColor(rState
.maStrokeColor
));
836 else if( rState
.meStrokeType
== DASH
)
838 xAttrs
->AddAttribute( "draw:stroke", "dash");
839 xAttrs
->AddAttribute( "draw:stroke-dash", "dash"+OUString::number(mnCurrStateId
));
840 xAttrs
->AddAttribute( "svg:stroke-color", getOdfColor(rState
.maStrokeColor
));
843 xAttrs
->AddAttribute( "draw:stroke", "none");
845 if( maCurrState
.mnStrokeWidth
!= 0.0 )
847 ::basegfx::B2DVector
aVec(maCurrState
.mnStrokeWidth
,0);
848 aVec
*= maCurrState
.maCTM
;
849 xAttrs
->AddAttribute( "svg:stroke-width", OUString::number( pt2mm(aVec
.getLength()) )+"mm");
851 if( maCurrState
.meLineJoin
== basegfx::B2DLineJoin::Miter
)
852 xAttrs
->AddAttribute( "draw:stroke-linejoin", "miter");
853 else if( maCurrState
.meLineJoin
== basegfx::B2DLineJoin::Round
)
854 xAttrs
->AddAttribute( "draw:stroke-linejoin", "round");
855 else if( maCurrState
.meLineJoin
== basegfx::B2DLineJoin::Bevel
)
856 xAttrs
->AddAttribute( "draw:stroke-linejoin", "bevel");
857 if( maCurrState
.mnStrokeOpacity
*maCurrState
.mnOpacity
!= 1.0 )
858 xAttrs
->AddAttribute( "svg:stroke-opacity",
859 OUString::number(100.0*maCurrState
.mnStrokeOpacity
*maCurrState
.mnOpacity
)+"%");
862 mxDocumentHandler
->startElement( "style:graphic-properties", xUnoAttrs
);
863 mxDocumentHandler
->endElement( "style:graphic-properties" );
864 mxDocumentHandler
->endElement( "style:style" );
866 return true; // newly written
869 void writeStyle(const uno::Reference
<xml::dom::XElement
>& xElem
, const sal_Int32 nTagId
)
871 SAL_INFO ("svg", "writeStyle xElem " << xElem
->getTagName());
873 sal_Int32 nStyleId
=0;
874 if( writeStyle(maCurrState
, nTagId
) )
875 nStyleId
= mnCurrStateId
;
877 nStyleId
= mrStates
.find(maCurrState
)->mnStyleId
;
879 xElem
->setAttribute("internal-style-ref",
887 maParentStates
.push_back(maCurrState
);
892 maParentStates
.pop_back();
895 void parseLinearGradientData( Gradient
& io_rCurrGradient
,
896 const sal_Int32 nGradientNumber
,
897 const sal_Int32 nTokenId
,
898 const OUString
& sValue
)
902 case XML_GRADIENTTRANSFORM
:
904 OString
aValueUtf8( sValue
.getStr(),
906 RTL_TEXTENCODING_UTF8
);
907 parseTransform(aValueUtf8
.getStr(),io_rCurrGradient
.maTransform
);
911 io_rCurrGradient
.maCoords
.linear
.mfX1
= convLength(sValue
,maCurrState
,'h');
914 io_rCurrGradient
.maCoords
.linear
.mfX2
= convLength(sValue
,maCurrState
,'h');
917 io_rCurrGradient
.maCoords
.linear
.mfY1
= convLength(sValue
,maCurrState
,'v');
920 io_rCurrGradient
.maCoords
.linear
.mfY2
= convLength(sValue
,maCurrState
,'v');
923 maGradientIdMap
.insert(std::make_pair(sValue
,nGradientNumber
));
925 case XML_GRADIENTUNITS
:
926 if (getTokenId(sValue
) == XML_OBJECTBOUNDINGBOX
)
927 io_rCurrGradient
.mbBoundingBoxUnits
= true;
929 io_rCurrGradient
.mbBoundingBoxUnits
= false;
936 void parseRadialGradientData( Gradient
& io_rCurrGradient
,
937 const sal_Int32 nGradientNumber
,
938 const sal_Int32 nTokenId
,
939 const OUString
& sValue
)
943 case XML_GRADIENTTRANSFORM
:
945 OString
aValueUtf8( sValue
.getStr(),
947 RTL_TEXTENCODING_UTF8
);
948 parseTransform(aValueUtf8
.getStr(),io_rCurrGradient
.maTransform
);
952 io_rCurrGradient
.maCoords
.radial
.mfCX
= convLength(sValue
,maCurrState
,'h');
955 io_rCurrGradient
.maCoords
.radial
.mfCY
= convLength(sValue
,maCurrState
,'v');
958 io_rCurrGradient
.maCoords
.radial
.mfFX
= convLength(sValue
,maCurrState
,'h');
961 io_rCurrGradient
.maCoords
.radial
.mfFY
= convLength(sValue
,maCurrState
,'v');
964 io_rCurrGradient
.maCoords
.radial
.mfR
= convLength(sValue
,maCurrState
,'r');
967 maGradientIdMap
.insert(std::make_pair(sValue
,nGradientNumber
));
969 case XML_GRADIENTUNITS
:
970 if (getTokenId(sValue
) == XML_OBJECTBOUNDINGBOX
)
971 io_rCurrGradient
.mbBoundingBoxUnits
= true;
973 io_rCurrGradient
.mbBoundingBoxUnits
= false;
980 void parseGradientStop( GradientStop
& io_rGradientStop
,
981 const sal_Int32 nStopNumber
,
982 const sal_Int32 nTokenId
,
983 const OUString
& sValue
)
989 ElementRefMapType::iterator aFound
=maStopIdMap
.end();
990 if ( sValue
.copy(0,1) == "#" )
991 aFound
= maStopIdMap
.find(sValue
.copy(1));
993 aFound
= maStopIdMap
.find(sValue
);
995 if( aFound
!= maStopIdMap
.end() )
996 io_rGradientStop
= maGradientStopVector
[aFound
->second
];
1000 maStopIdMap
.insert(std::make_pair(sValue
,nStopNumber
));
1002 case XML_STOP_COLOR
:
1003 if( maGradientVector
.empty() ||
1004 maGradientVector
.back().maStops
.empty() )
1006 parseColor( sValue
.toUtf8().getStr(), maGradientStopVector
[
1007 maGradientVector
.back().maStops
.back()].maStopColor
);
1009 case XML_STOP_OPACITY
:
1010 if( maGradientVector
.empty() ||
1011 maGradientVector
.back().maStops
.empty() )
1013 parseOpacity( sValue
.toUtf8().getStr(),
1014 maGradientStopVector
[
1015 maGradientVector
.back().maStops
.back()].maStopColor
);
1018 io_rGradientStop
.mnStopPosition
= sValue
.toDouble();
1021 parseStyle( sValue
);
1028 void parseAttribute( const sal_Int32 nTokenId
,
1029 const OUString
& sValue
)
1031 OString
aValueUtf8( sValue
.getStr(),
1033 RTL_TEXTENCODING_UTF8
);
1038 const double fViewPortWidth(
1039 convLength(sValue
,maCurrState
,'h'));
1041 maCurrState
.maViewport
.expand(
1042 basegfx::B2DTuple(fViewPortWidth
,0.0));
1047 const double fViewPortHeight(
1048 convLength(sValue
,maCurrState
,'v'));
1050 maCurrState
.maViewport
.expand(
1051 basegfx::B2DTuple(0.0,fViewPortHeight
));
1056 // TODO(F1): preserveAspectRatio
1058 aValueUtf8
.getStr(),
1059 maCurrState
.maViewBox
);
1064 if( aValueUtf8
== "evenodd" )
1065 maCurrState
.meFillRule
= EVEN_ODD
;
1066 else if( aValueUtf8
== "nonzero" )
1067 maCurrState
.meFillRule
= NON_ZERO
;
1068 else if( aValueUtf8
== "inherit" )
1069 maCurrState
.meFillRule
= maParentStates
.back().meFillRule
;
1073 if( aValueUtf8
== "inherit" )
1074 maCurrState
.mnOpacity
= maParentStates
.back().mnOpacity
;
1076 maCurrState
.mnOpacity
= aValueUtf8
.toDouble();
1078 case XML_FILL_OPACITY
:
1079 if( aValueUtf8
== "inherit" )
1080 maCurrState
.mnFillOpacity
= maParentStates
.back().mnFillOpacity
;
1082 maCurrState
.mnFillOpacity
= aValueUtf8
.toDouble();
1083 if( maCurrState
.mnFillOpacity
> 1 )
1084 maCurrState
.mnFillOpacity
= 1;
1087 case XML_STROKE_WIDTH
:
1089 if( aValueUtf8
== "inherit" )
1090 maCurrState
.mnStrokeWidth
= maParentStates
.back().mnStrokeWidth
;
1092 maCurrState
.mnStrokeWidth
= convLength(sValue
,maCurrState
,'r');
1095 case XML_STROKE_LINECAP
:
1097 if( aValueUtf8
== "butt" )
1098 maCurrState
.meLineCap
= BUTT
;
1099 else if( aValueUtf8
== "round" )
1100 maCurrState
.meLineCap
= ROUND
;
1101 else if( aValueUtf8
== "square" )
1102 maCurrState
.meLineCap
= RECT
;
1103 else if( aValueUtf8
== "inherit" )
1104 maCurrState
.meLineCap
= maParentStates
.back().meLineCap
;
1107 case XML_STROKE_LINEJOIN
:
1109 if( aValueUtf8
== "miter" )
1110 maCurrState
.meLineJoin
= basegfx::B2DLineJoin::Miter
;
1111 else if( aValueUtf8
== "round" )
1112 maCurrState
.meLineJoin
= basegfx::B2DLineJoin::Round
;
1113 else if( aValueUtf8
== "bevel" )
1114 maCurrState
.meLineJoin
= basegfx::B2DLineJoin::Bevel
;
1115 else if( aValueUtf8
== "inherit" )
1116 maCurrState
.meLineJoin
= maParentStates
.back().meLineJoin
;
1119 case XML_STROKE_MITERLIMIT
:
1121 if( aValueUtf8
== "inherit" )
1122 maCurrState
.mnMiterLimit
= maParentStates
.back().mnMiterLimit
;
1124 maCurrState
.mnMiterLimit
= aValueUtf8
.toDouble();
1127 case XML_STROKE_DASHOFFSET
:
1129 if( aValueUtf8
== "inherit" )
1130 maCurrState
.mnDashOffset
= maParentStates
.back().mnDashOffset
;
1132 maCurrState
.mnDashOffset
= convLength(sValue
,maCurrState
,'r');
1135 case XML_STROKE_DASHARRAY
:
1137 if( aValueUtf8
== "none" )
1139 maCurrState
.maDashArray
.clear();
1140 maCurrState
.meStrokeType
= SOLID
;
1142 else if( aValueUtf8
== "inherit" )
1143 maCurrState
.maDashArray
= maParentStates
.back().maDashArray
;
1146 if( parseDashArray(aValueUtf8
.getStr(),
1147 maCurrState
.maDashArray
) )
1149 maCurrState
.meStrokeType
= DASH
;
1153 maCurrState
.meStrokeType
= SOLID
;
1158 case XML_STROKE_OPACITY
:
1159 if( aValueUtf8
== "inherit" )
1160 maCurrState
.mnStrokeOpacity
= maParentStates
.back().mnStrokeOpacity
;
1162 maCurrState
.mnStrokeOpacity
= aValueUtf8
.toDouble();
1166 const State
& rParent( maParentStates
.back() );
1168 aValueUtf8
.getStr(),
1169 maCurrState
.meFillType
,
1170 maCurrState
.maFillColor
,
1171 maCurrState
.maFillGradient
,
1173 rParent
.maFillColor
,
1174 rParent
.maFillGradient
);
1179 const State
& rParent( maParentStates
.back() );
1181 aValueUtf8
.getStr(),
1182 maCurrState
.meStrokeType
,
1183 maCurrState
.maStrokeColor
,
1184 maCurrState
.maStrokeGradient
,
1185 rParent
.meStrokeType
,
1186 rParent
.maStrokeColor
,
1187 rParent
.maStrokeGradient
);
1192 if( aValueUtf8
== "inherit" )
1193 maCurrState
.maCurrentColor
= maParentStates
.back().maCurrentColor
;
1195 parseColor(aValueUtf8
.getStr(), maCurrState
.maCurrentColor
);
1200 basegfx::B2DHomMatrix aTransform
;
1201 parseTransform(aValueUtf8
.getStr(),aTransform
);
1202 maCurrState
.maTransform
= maCurrState
.maTransform
*aTransform
;
1205 case XML_FONT_FAMILY
:
1206 maCurrState
.maFontFamily
=sValue
;
1209 maCurrState
.mnParentFontSize
=maParentStates
.back().mnFontSize
;
1210 maCurrState
.mnFontSize
=convLength(sValue
,maCurrState
,'v');
1212 case XML_FONT_STYLE
:
1213 parseFontStyle(maCurrState
,sValue
,aValueUtf8
.getStr());
1215 case XML_FONT_WEIGHT
:
1216 maCurrState
.mnFontWeight
=sValue
.toDouble();
1218 case XML_FONT_VARIANT
:
1219 parseFontVariant(maCurrState
,sValue
,aValueUtf8
.getStr());
1221 case XML_TEXT_ANCHOR
:
1222 parseTextAlign(maCurrState
,aValueUtf8
.getStr());
1224 case XML_STOP_COLOR
:
1225 case XML_STOP_OPACITY
:
1226 parseGradientStop( maGradientStopVector
.back(),
1227 maGradientStopVector
.size()-1,
1230 case XML_TOKEN_INVALID
:
1231 SAL_INFO("svg", "unhandled token");
1234 SAL_INFO("svg", "unhandled token " << getTokenName(nTokenId
));
1239 void parseStyle( const OUString
& sValue
)
1241 // split individual style attributes
1242 sal_Int32 nIndex
=0, nDummyIndex
=0;
1243 OUString aCurrToken
;
1246 aCurrToken
=sValue
.getToken(0,';',nIndex
);
1248 if( !aCurrToken
.isEmpty() )
1250 // split attrib & value
1252 OUString
aCurrAttrib(
1253 aCurrToken
.getToken(0,':',nDummyIndex
).trim());
1254 OSL_ASSERT(nDummyIndex
!=-1);
1256 OUString
aCurrValue(
1257 aCurrToken
.getToken(1,':',nDummyIndex
).trim());
1258 OSL_ASSERT(nDummyIndex
==-1);
1260 // recurse into normal attribute parsing
1261 parseAttribute( getTokenId(aCurrAttrib
),
1265 while( nIndex
!= -1 );
1268 static void parseFontStyle( State
& io_rInitialState
,
1269 const OUString
& rValue
,
1270 const char* sValue
)
1272 if( strcmp(sValue
,"inherit") != 0 )
1273 io_rInitialState
.maFontStyle
= rValue
;
1276 static void parseFontVariant( State
& io_rInitialState
,
1277 const OUString
& rValue
,
1278 const char* sValue
)
1280 if( strcmp(sValue
,"inherit") != 0 )
1281 io_rInitialState
.maFontVariant
= rValue
;
1284 static void parseTextAlign( State
& io_rInitialState
,
1285 const char* sValue
)
1287 if( strcmp(sValue
,"start") == 0 )
1288 io_rInitialState
.meTextAnchor
= BEFORE
;
1289 else if( strcmp(sValue
,"middle") == 0 )
1290 io_rInitialState
.meTextAnchor
= CENTER
;
1291 else if( strcmp(sValue
,"end") == 0 )
1292 io_rInitialState
.meTextAnchor
= AFTER
;
1293 // keep current val for sValue == "inherit"
1296 void parsePaint( const OUString
& rValue
,
1300 Gradient
& rGradient
,
1301 const PaintType
& rInheritType
,
1302 const ARGBColor
& rInheritColor
,
1303 const Gradient
& rInheritGradient
)
1305 std::pair
<const char*,const char*> aPaintUri(nullptr,nullptr);
1306 std::pair
<ARGBColor
,bool> aColor(maCurrState
.maCurrentColor
,
1308 if( strcmp(sValue
,"none") == 0 )
1310 else if( strcmp(sValue
,"currentColor") == 0 )
1313 rColor
= maCurrState
.maCurrentColor
;
1315 else if( strcmp(sValue
,"inherit") == 0)
1317 rType
= rInheritType
;
1318 rColor
= rInheritColor
;
1319 rGradient
= rInheritGradient
;
1321 else if( parsePaintUri(aPaintUri
,aColor
,sValue
) )
1323 if( aPaintUri
.first
!= aPaintUri
.second
)
1325 // assuming gradient. assumption does not hold generally
1326 if( strstr(sValue
,")") && rValue
.getLength() > 5 )
1328 ElementRefMapType::iterator aRes
;
1329 if( (aRes
=maGradientIdMap
.find(
1330 rValue
.copy(aPaintUri
.first
-sValue
,
1331 aPaintUri
.second
-aPaintUri
.first
))) != maGradientIdMap
.end() )
1333 rGradient
= maGradientVector
[aRes
->second
];
1338 else if( aColor
.second
)
1341 rColor
= aColor
.first
;
1351 parseColor(sValue
,rColor
);
1355 sal_Int32 mnCurrStateId
;
1357 std::vector
<State
> maParentStates
;
1358 StatePool
& mrStates
;
1359 StateMap
& mrStateMap
;
1360 uno::Reference
<xml::sax::XDocumentHandler
> mxDocumentHandler
;
1361 std::vector
< Gradient
> maGradientVector
;
1362 std::vector
< GradientStop
> maGradientStopVector
;
1363 std::vector
< uno::Reference
<xml::dom::XElement
> > maElementVector
;
1364 std::vector
< uno::Reference
<xml::dom::XElement
> >& mrUseElementVector
;
1365 ElementRefMapType maGradientIdMap
;
1366 ElementRefMapType maStopIdMap
;
1367 ElementRefMapType maElementIdMap
;
1368 bool& mrGradientNotFound
;
1371 /// Annotate svg styles with unique references to state pool
1372 void annotateStyles( StatePool
& rStatePool
,
1373 StateMap
& rStateMap
,
1374 const State
& rInitialState
,
1375 uno::Reference
<xml::dom::XElement
>& rElem
,
1376 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocHdl
,
1377 std::vector
< uno::Reference
<xml::dom::XElement
> >& rUseElementVector
)
1379 bool bGradientNotFound
= false;
1380 AnnotatingVisitor
aVisitor(rStatePool
, rStateMap
, rInitialState
, xDocHdl
, rUseElementVector
, bGradientNotFound
);
1381 visitElements(aVisitor
, rElem
, STYLE_ANNOTATOR
);
1383 //Sometimes, xlink:href in gradients refers to another gradient which hasn't been parsed yet.
1384 // if that happens, we'll need to parse the styles again, so everything gets referred.
1385 if( bGradientNotFound
)
1387 visitElements(aVisitor
, rElem
, STYLE_ANNOTATOR
);
1391 struct ShapeWritingVisitor
1393 ShapeWritingVisitor(StatePool
& /*rStatePool*/,
1394 StateMap
& rStateMap
,
1395 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocumentHandler
) :
1396 mrStateMap(rStateMap
),
1397 mxDocumentHandler(xDocumentHandler
),
1401 void operator()( const uno::Reference
<xml::dom::XElement
>& )
1405 void operator()( const uno::Reference
<xml::dom::XElement
>& xElem
,
1406 const uno::Reference
<xml::dom::XNamedNodeMap
>& xAttributes
)
1408 rtl::Reference
<SvXMLAttributeList
> xAttrs( new SvXMLAttributeList() );
1409 uno::Reference
<xml::sax::XAttributeList
> xUnoAttrs( xAttrs
.get() );
1411 sal_Int32
nDummyIndex(0);
1413 xElem
->getAttribute("internal-style-ref").getToken(
1414 0,'$',nDummyIndex
));
1415 StateMap::iterator pOrigState
=mrStateMap
.find(
1416 sStyleId
.toInt32());
1418 if( pOrigState
== mrStateMap
.end() )
1419 return; // non-exportable element, e.g. linearGradient
1421 maCurrState
= pOrigState
->second
;
1423 const sal_Int32
nTokenId(getTokenId(xElem
->getNodeName()));
1428 // collect attributes
1429 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
1430 OUString sAttributeValue
;
1431 double x1
=0.0,y1
=0.0,x2
=0.0,y2
=0.0;
1432 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
1434 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
1435 const sal_Int32
nAttribId(
1436 getTokenId(xAttributes
->item(i
)->getNodeName()));
1440 x1
= convLength(sAttributeValue
,maCurrState
,'h');
1443 x2
= convLength(sAttributeValue
,maCurrState
,'h');
1446 y1
= convLength(sAttributeValue
,maCurrState
,'v');
1449 y2
= convLength(sAttributeValue
,maCurrState
,'v');
1457 if ( x1
!= x2
|| y1
!= y2
) {
1458 OUString sLinePath
= "M"+OUString::number(x1
)+","
1459 +OUString::number(y1
)+"L"+OUString::number(x2
)+","
1460 +OUString::number(y2
);
1461 basegfx::B2DPolyPolygon aPoly
;
1462 basegfx::tools::importFromSvgD(aPoly
, sLinePath
, false, nullptr);
1464 writePathShape(xAttrs
,
1468 basegfx::B2DPolyPolygon(aPoly
));
1476 OUString sPoints
= xElem
->hasAttribute("points") ? xElem
->getAttribute("points") : "";
1477 basegfx::B2DPolygon aPoly
;
1478 (void)basegfx::tools::importFromSvgPoints(aPoly
, sPoints
);
1479 if( nTokenId
== XML_POLYGON
|| maCurrState
.meFillType
!= NONE
)
1480 aPoly
.setClosed(true);
1482 writePathShape(xAttrs
,
1486 basegfx::B2DPolyPolygon(aPoly
));
1491 // collect attributes
1492 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
1493 OUString sAttributeValue
;
1494 bool bRxSeen
=false, bRySeen
=false;
1495 double x
=0.0,y
=0.0,width
=0.0,height
=0.0,rx
=0.0,ry
=0.0;
1496 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
1498 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
1499 const sal_Int32
nAttribId(
1500 getTokenId(xAttributes
->item(i
)->getNodeName()));
1504 x
= convLength(sAttributeValue
,maCurrState
,'h');
1507 y
= convLength(sAttributeValue
,maCurrState
,'v');
1510 width
= convLength(sAttributeValue
,maCurrState
,'h');
1513 height
= convLength(sAttributeValue
,maCurrState
,'v');
1516 rx
= convLength(sAttributeValue
,maCurrState
,'h');
1520 ry
= convLength(sAttributeValue
,maCurrState
,'v');
1529 if ( (width
> 0) && (height
> 0) ) {
1530 if( bRxSeen
&& !bRySeen
)
1532 else if( bRySeen
&& !bRxSeen
)
1535 basegfx::B2DPolygon aPoly
;
1536 aPoly
= basegfx::tools::createPolygonFromRect(
1537 basegfx::B2DRange(x
,y
,x
+width
,y
+height
),
1538 rx
/(0.5*width
), ry
/(0.5*height
) );
1540 writePathShape(xAttrs
,
1544 basegfx::B2DPolyPolygon(aPoly
));
1550 OUString sPath
= xElem
->hasAttribute("d") ? xElem
->getAttribute("d") : "";
1551 basegfx::B2DPolyPolygon aPoly
;
1552 basegfx::tools::importFromSvgD(aPoly
, sPath
, false, nullptr);
1554 if ((maCurrState
.meStrokeType
== NONE
) &&
1555 (maCurrState
.meFillType
!= NONE
) &&
1558 aPoly
.setClosed(true);
1561 // tdf#51165: rendering of paths with open and closed polygons is not implemented
1562 // split mixed polypolygons into single polygons and add them one by one
1563 if( PolyPolygonIsMixedOpenAndClosed(aPoly
) )
1565 for( sal_uInt32
i(0L); i
<aPoly
.count(); ++i
) {
1566 writePathShape(xAttrs
,
1570 basegfx::B2DPolyPolygon(aPoly
.getB2DPolygon(i
)));
1575 writePathShape(xAttrs
,
1585 // collect attributes
1586 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
1587 OUString sAttributeValue
;
1588 double cx
=0.0,cy
=0.0,r
=0.0;
1589 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
1591 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
1592 const sal_Int32
nAttribId(
1593 getTokenId(xAttributes
->item(i
)->getNodeName()));
1597 cx
= convLength(sAttributeValue
,maCurrState
,'h');
1600 cy
= convLength(sAttributeValue
,maCurrState
,'v');
1603 r
= convLength(sAttributeValue
,maCurrState
,'r');
1612 writeEllipseShape(xAttrs
,
1616 basegfx::B2DEllipse(basegfx::B2DPoint(cx
, cy
), basegfx::B2DTuple(r
,r
)));
1621 // collect attributes
1622 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
1623 OUString sAttributeValue
;
1624 double cx
=0.0,cy
=0.0,rx
=0.0, ry
=0.0;
1625 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
1627 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
1628 const sal_Int32
nAttribId(
1629 getTokenId(xAttributes
->item(i
)->getNodeName()));
1633 cx
= convLength(sAttributeValue
,maCurrState
,'h');
1636 cy
= convLength(sAttributeValue
,maCurrState
,'v');
1639 rx
= convLength(sAttributeValue
,maCurrState
,'h');
1642 ry
= convLength(sAttributeValue
,maCurrState
,'v');
1650 if ( rx
> 0 && ry
> 0 )
1651 writeEllipseShape(xAttrs
,
1655 basegfx::B2DEllipse(basegfx::B2DPoint(cx
, cy
), basegfx::B2DTuple(rx
,ry
)));
1660 // collect attributes
1661 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
1662 OUString sAttributeValue
;
1663 double x
=0.0, y
=0.0, width
=0.0, height
=0.0;
1664 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
1666 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
1667 const sal_Int32
nAttribId(
1668 getTokenId(xAttributes
->item(i
)->getNodeName()));
1672 x
= convLength(sAttributeValue
,maCurrState
,'h');
1675 y
= convLength(sAttributeValue
,maCurrState
,'v');
1678 width
= convLength(sAttributeValue
,maCurrState
,'h');
1681 height
= convLength(sAttributeValue
,maCurrState
,'v');
1688 // extract basic transformations out of CTM
1689 basegfx::B2DTuple aScale
, aTranslate
;
1690 double fRotate
, fShearX
;
1691 if (maCurrState
.maCTM
.decompose(aScale
, aTranslate
, fRotate
, fShearX
))
1695 width
*= aScale
.getX();
1697 height
*= aScale
.getY();
1698 x
+= aTranslate
.getX();
1699 y
+= aTranslate
.getY();
1703 OUString sValue
= xElem
->hasAttribute("href") ? xElem
->getAttribute("href") : "";
1704 OString
aValueUtf8( sValue
.getStr(), sValue
.getLength(), RTL_TEXTENCODING_UTF8
);
1705 OUString sLinkValue
;
1706 parseXlinkHref(aValueUtf8
.getStr(), sLinkValue
);
1708 if (!sLinkValue
.isEmpty())
1709 writeBinaryData(xAttrs
, xUnoAttrs
, xElem
, basegfx::B2DRange(x
,y
,x
+width
,y
+height
), sLinkValue
);
1715 // collect text from all TEXT_NODE children into sText
1716 OUStringBuffer sText
;
1718 [&sText
] (xml::dom::XNode
& rNode
) {
1719 return sText
.append(rNode
.getNodeValue());
1722 xml::dom::NodeType_TEXT_NODE
);
1724 // collect attributes
1725 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
1726 OUString sAttributeValue
;
1728 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
1730 sAttributeValue
= xAttributes
->item(i
)->getNodeValue();
1731 const sal_Int32
nAttribId(
1732 getTokenId(xAttributes
->item(i
)->getNodeName()));
1736 x
= convLength(sAttributeValue
,maCurrState
,'h');
1739 y
= convLength(sAttributeValue
,maCurrState
,'v');
1747 // actually export text
1751 // extract basic transformations out of CTM
1752 basegfx::B2DTuple aScale
, aTranslate
;
1753 double fRotate
, fShearX
;
1754 if (maCurrState
.maCTM
.decompose(aScale
, aTranslate
, fRotate
, fShearX
))
1756 // some heuristic attempts to have text output
1757 // baseline-relative
1758 y
-= 2.0*maCurrState
.mnFontSize
/aScale
.getX()/3.0;
1762 x
+= aTranslate
.getX();
1763 y
+= aTranslate
.getY();
1767 // some heuristic attempts to have text output
1768 // baseline-relative
1769 y
-= 2.0*maCurrState
.mnFontSize
/3.0;
1772 xAttrs
->AddAttribute( "svg:x", OUString::number(pt2mm(x
))+"mm");
1773 xAttrs
->AddAttribute( "svg:y", OUString::number(pt2mm(y
))+"mm");
1774 xAttrs
->AddAttribute( "draw:style-name", "svggraphicstyle"+sStyleId
);
1776 mxDocumentHandler
->startElement("draw:frame", xUnoAttrs
);
1779 mxDocumentHandler
->startElement("draw:text-box", xUnoAttrs
);
1780 xAttrs
->AddAttribute( "text:style-name", "svgparagraphstyle"+sStyleId
);
1781 mxDocumentHandler
->startElement("text:p", xUnoAttrs
);
1784 xAttrs
->AddAttribute( "text:style-name", "svgtextstyle"+sStyleId
);
1785 mxDocumentHandler
->startElement("text:span", xUnoAttrs
);
1788 mxDocumentHandler
->characters(sText
.makeStringAndClear());
1789 mxDocumentHandler
->endElement("text:span");
1790 mxDocumentHandler
->endElement("text:p");
1791 mxDocumentHandler
->endElement("draw:text-box");
1792 mxDocumentHandler
->endElement("draw:frame");
1804 void writeBinaryData( rtl::Reference
<SvXMLAttributeList
>& xAttrs
,
1805 const uno::Reference
<xml::sax::XAttributeList
>& xUnoAttrs
,
1806 const uno::Reference
<xml::dom::XElement
>& /* xElem */,
1807 const basegfx::B2DRange
& rShapeBounds
,
1808 const OUString
& data
)
1811 xAttrs
->AddAttribute( "svg:x", OUString::number(pt2mm(rShapeBounds
.getMinX()))+"mm");
1812 xAttrs
->AddAttribute( "svg:y", OUString::number(pt2mm(rShapeBounds
.getMinY()))+"mm");
1813 xAttrs
->AddAttribute( "svg:width", OUString::number(pt2mm(rShapeBounds
.getWidth()))+"mm");
1814 xAttrs
->AddAttribute( "svg:height", OUString::number(pt2mm(rShapeBounds
.getHeight()))+"mm");
1816 mxDocumentHandler
->startElement("draw:frame", xUnoAttrs
);
1819 mxDocumentHandler
->startElement("draw:image", xUnoAttrs
);
1821 mxDocumentHandler
->startElement("office:binary-data", xUnoAttrs
);
1823 mxDocumentHandler
->characters(data
);
1825 mxDocumentHandler
->endElement("office:binary-data");
1827 mxDocumentHandler
->endElement("draw:image");
1829 mxDocumentHandler
->endElement("draw:frame");
1833 void writeEllipseShape( rtl::Reference
<SvXMLAttributeList
>& xAttrs
,
1834 const uno::Reference
<xml::sax::XAttributeList
>& xUnoAttrs
,
1835 const uno::Reference
<xml::dom::XElement
>& xElem
,
1836 const OUString
& rStyleId
,
1837 const basegfx::B2DEllipse
& rEllipse
)
1839 State aState
= maCurrState
;
1843 basegfx::B2DPolygon aPoly
= basegfx::tools::createPolygonFromEllipse(rEllipse
.getB2DEllipseCenter(),
1844 rEllipse
.getB2DEllipseRadius().getX(), rEllipse
.getB2DEllipseRadius().getY());
1845 writePathShape(xAttrs
, xUnoAttrs
, xElem
, rStyleId
, basegfx::B2DPolyPolygon(aPoly
));
1849 void writePathShape( rtl::Reference
<SvXMLAttributeList
>& xAttrs
,
1850 const uno::Reference
<xml::sax::XAttributeList
>& xUnoAttrs
,
1851 const uno::Reference
<xml::dom::XElement
>& xElem
,
1852 const OUString
& rStyleId
,
1853 const basegfx::B2DPolyPolygon
& rPoly
)
1855 // we might need to split up polypolygon into multiple path
1856 // shapes (e.g. when emulating line stroking)
1857 std::vector
<basegfx::B2DPolyPolygon
> aPolys(1,rPoly
);
1858 State aState
= maCurrState
;
1862 OSL_TRACE("writePath - the CTM is: %f %f %f %f %f %f",
1863 maCurrState
.maCTM
.get(0,0),
1864 maCurrState
.maCTM
.get(0,1),
1865 maCurrState
.maCTM
.get(0,2),
1866 maCurrState
.maCTM
.get(1,0),
1867 maCurrState
.maCTM
.get(1,1),
1868 maCurrState
.maCTM
.get(1,2));
1870 // TODO(F2): separate out shear, rotate etc.
1871 // apply transformation to polygon, to keep draw
1872 // import in 100th mm
1873 for (basegfx::B2DPolyPolygon
& aPoly
: aPolys
)
1875 aPoly
.transform(aState
.maCTM
);
1878 for(basegfx::B2DPolyPolygon
& aPoly
: aPolys
)
1880 const basegfx::B2DRange
aBounds(
1881 aPoly
.areControlPointsUsed() ?
1882 basegfx::tools::getRange(
1883 basegfx::tools::adaptiveSubdivideByAngle(aPoly
)) :
1884 basegfx::tools::getRange(aPoly
));
1885 fillShapeProperties(xAttrs
,
1888 "svggraphicstyle"+rStyleId
);
1890 // force path coordinates to 100th millimeter, after
1891 // putting polygon data at origin (ODF viewbox
1892 // calculations largely untested codepaths, as OOo always
1893 // writes "0 0 w h" viewboxes)
1894 basegfx::B2DHomMatrix aNormalize
;
1895 aNormalize
.translate(-aBounds
.getMinX(),-aBounds
.getMinY());
1896 aNormalize
.scale(2540.0/72.0,2540.0/72.0);
1897 aPoly
.transform(aNormalize
);
1899 xAttrs
->AddAttribute( "svg:d", basegfx::tools::exportToSvgD(
1901 false, // no relative coords. causes rounding errors
1902 false, // no quad bezier detection. crashes older versions.
1904 mxDocumentHandler
->startElement("draw:path", xUnoAttrs
);
1905 mxDocumentHandler
->endElement("draw:path");
1909 void fillShapeProperties( rtl::Reference
<SvXMLAttributeList
>& xAttrs
,
1910 const uno::Reference
<xml::dom::XElement
>& /* xElem */,
1911 const basegfx::B2DRange
& rShapeBounds
,
1912 const OUString
& rStyleName
)
1914 xAttrs
->AddAttribute( "draw:z-index", OUString::number( mnShapeNum
++ ));
1915 xAttrs
->AddAttribute( "draw:style-name", rStyleName
);
1916 xAttrs
->AddAttribute( "svg:width", OUString::number(pt2mm(rShapeBounds
.getWidth()))+"mm");
1917 xAttrs
->AddAttribute( "svg:height", OUString::number(pt2mm(rShapeBounds
.getHeight()))+"mm");
1919 // OOo expects the viewbox to be in 100th of mm
1920 xAttrs
->AddAttribute( "svg:viewBox",
1923 basegfx::fround(pt100thmm(rShapeBounds
.getWidth())) )
1926 basegfx::fround(pt100thmm(rShapeBounds
.getHeight())) ));
1928 // TODO(F1): decompose transformation in calling code, and use
1929 // transform attribute here
1930 // writeTranslate(maCurrState.maCTM, xAttrs);
1931 xAttrs
->AddAttribute( "svg:x", OUString::number(pt2mm(rShapeBounds
.getMinX()))+"mm");
1932 xAttrs
->AddAttribute( "svg:y", OUString::number(pt2mm(rShapeBounds
.getMinY()))+"mm");
1936 StateMap
& mrStateMap
;
1937 uno::Reference
<xml::sax::XDocumentHandler
> mxDocumentHandler
;
1938 sal_Int32 mnShapeNum
;
1941 /// Write out shapes from DOM tree
1942 void writeShapes( StatePool
& rStatePool
,
1943 StateMap
& rStateMap
,
1944 const uno::Reference
<xml::dom::XElement
>& rElem
,
1945 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocHdl
,
1946 std::vector
< uno::Reference
<xml::dom::XElement
> >& rUseElementVector
)
1948 ShapeWritingVisitor
aVisitor(rStatePool
,rStateMap
,xDocHdl
);
1949 visitElements(aVisitor
, rElem
, SHAPE_WRITER
);
1951 std::vector
< uno::Reference
<xml::dom::XElement
> >::iterator it
;
1952 for ( it
= rUseElementVector
.begin() ; it
!= rUseElementVector
.end(); ++it
)
1954 visitElements(aVisitor
, *it
, SHAPE_WRITER
);
1960 struct OfficeStylesWritingVisitor
1962 OfficeStylesWritingVisitor( StateMap
& rStateMap
,
1963 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocumentHandler
) :
1964 mrStateMap(rStateMap
),
1965 mxDocumentHandler(xDocumentHandler
)
1967 void operator()( const uno::Reference
<xml::dom::XElement
>& /*xElem*/ )
1970 void operator()( const uno::Reference
<xml::dom::XElement
>& xElem
,
1971 const uno::Reference
<xml::dom::XNamedNodeMap
>& /*xAttributes*/ )
1973 rtl::Reference
<SvXMLAttributeList
> xAttrs( new SvXMLAttributeList() );
1974 uno::Reference
<xml::sax::XAttributeList
> xUnoAttrs( xAttrs
.get() );
1976 sal_Int32
nDummyIndex(0);
1978 xElem
->getAttribute("internal-style-ref").getToken(
1979 0,'$',nDummyIndex
));
1980 StateMap::iterator pOrigState
=mrStateMap
.find(
1981 sStyleId
.toInt32());
1983 if( pOrigState
== mrStateMap
.end() )
1984 return; // non-exportable element, e.g. linearGradient
1986 maCurrState
= pOrigState
->second
;
1988 if( maCurrState
.meStrokeType
== DASH
)
1990 sal_Int32 dots1
, dots2
;
1991 double dots1_length
, dots2_length
, dash_distance
;
1992 SvgDashArray2Odf( &dots1
, &dots1_length
, &dots2
, &dots2_length
, &dash_distance
);
1995 xAttrs
->AddAttribute( "draw:name", "dash"+sStyleId
);
1996 xAttrs
->AddAttribute( "draw:display-name", "dash"+sStyleId
);
1997 xAttrs
->AddAttribute( "draw:style", "rect" );
1999 xAttrs
->AddAttribute( "draw:dots1", OUString::number(dots1
) );
2000 xAttrs
->AddAttribute( "draw:dots1-length", OUString::number(pt2mm(convLength( OUString::number(dots1_length
), maCurrState
, 'h' )))+"mm" );
2002 xAttrs
->AddAttribute( "draw:distance", OUString::number(pt2mm(convLength( OUString::number(dash_distance
), maCurrState
, 'h' )))+"mm" );
2004 xAttrs
->AddAttribute( "draw:dots2", OUString::number(dots2
) );
2005 xAttrs
->AddAttribute( "draw:dots2-length", OUString::number(pt2mm(convLength( OUString::number(dots2_length
), maCurrState
, 'h' )))+"mm" );
2008 mxDocumentHandler
->startElement( "draw:stroke-dash", xUnoAttrs
);
2009 mxDocumentHandler
->endElement( "draw:stroke-dash" );
2013 void SvgDashArray2Odf( sal_Int32
*dots1
, double *dots1_length
, sal_Int32
*dots2
, double *dots2_length
, double *dash_distance
)
2021 if( maCurrState
.maDashArray
.empty() ) {
2025 double effective_dasharray_size
= maCurrState
.maDashArray
.size();
2026 if( maCurrState
.maDashArray
.size() % 2 == 1 )
2027 effective_dasharray_size
= maCurrState
.maDashArray
.size()*2;
2029 *dash_distance
= maCurrState
.maDashArray
[1%maCurrState
.maDashArray
.size()];
2030 sal_Int32 dist_count
= 1;
2031 for( int i
=3; i
<effective_dasharray_size
; i
+=2 ) {
2032 *dash_distance
= ((dist_count
* *dash_distance
) + maCurrState
.maDashArray
[i
%maCurrState
.maDashArray
.size()])/(dist_count
+1);
2037 *dots1_length
= maCurrState
.maDashArray
[0];
2039 while( ( i
<effective_dasharray_size
) && ( maCurrState
.maDashArray
[i
%maCurrState
.maDashArray
.size()] == *dots1_length
) ) {
2043 if( i
<effective_dasharray_size
) {
2045 *dots2_length
= maCurrState
.maDashArray
[i
];
2047 while( ( i
<effective_dasharray_size
) && ( maCurrState
.maDashArray
[i
%maCurrState
.maDashArray
.size()] == *dots2_length
) ) {
2053 SAL_INFO("svg", "SvgDashArray2Odf " << *dash_distance
<< " " << *dots1
<< " " << *dots1_length
<< " " << *dots2
<< " " << *dots2_length
);
2058 static void push() {}
2059 static void pop() {}
2062 StateMap
& mrStateMap
;
2063 uno::Reference
<xml::sax::XDocumentHandler
> mxDocumentHandler
;
2066 static void writeOfficeStyles( StateMap
& rStateMap
,
2067 const uno::Reference
<xml::dom::XElement
>& rElem
,
2068 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocHdl
)
2070 OfficeStylesWritingVisitor
aVisitor( rStateMap
, xDocHdl
);
2071 visitElements( aVisitor
, rElem
, STYLE_WRITER
);
2075 #ifdef DEBUG_FILTER_SVGREADER
2076 struct DumpingVisitor
2078 void operator()( const uno::Reference
<xml::dom::XElement
>& xElem
)
2080 OSL_TRACE("name: %s",
2082 xElem
->getTagName(),
2083 RTL_TEXTENCODING_UTF8
).getStr());
2086 void operator()( const uno::Reference
<xml::dom::XElement
>& xElem
,
2087 const uno::Reference
<xml::dom::XNamedNodeMap
>& xAttributes
)
2089 OSL_TRACE("name: %s",
2091 xElem
->getTagName(),
2092 RTL_TEXTENCODING_UTF8
).getStr());
2093 const sal_Int32
nNumAttrs( xAttributes
->getLength() );
2094 for( sal_Int32 i
=0; i
<nNumAttrs
; ++i
)
2098 xAttributes
->item(i
)->getNodeName(),
2099 RTL_TEXTENCODING_UTF8
).getStr(),
2101 xAttributes
->item(i
)->getNodeValue(),
2102 RTL_TEXTENCODING_UTF8
).getStr());
2110 static void dumpTree( const uno::Reference
<xml::dom::XElement
> xElem
)
2112 DumpingVisitor aVisitor
;
2113 visitElements(aVisitor
, xElem
, STYLE_ANNOTATOR
);
2118 SVGReader::SVGReader(const uno::Reference
<uno::XComponentContext
>& xContext
,
2119 const uno::Reference
<io::XInputStream
>& xInputStream
,
2120 const uno::Reference
<xml::sax::XDocumentHandler
>& xDocumentHandler
) :
2121 m_xContext( xContext
),
2122 m_xInputStream( xInputStream
),
2123 m_xDocumentHandler( xDocumentHandler
)
2127 bool SVGReader::parseAndConvert()
2129 uno::Reference
<xml::dom::XDocumentBuilder
> xDomBuilder
= xml::dom::DocumentBuilder::create(m_xContext
);
2131 uno::Reference
<xml::dom::XDocument
> xDom(
2132 xDomBuilder
->parse(m_xInputStream
),
2133 uno::UNO_QUERY_THROW
);
2135 uno::Reference
<xml::dom::XElement
> xDocElem( xDom
->getDocumentElement(),
2136 uno::UNO_QUERY_THROW
);
2138 // the root state for svg document
2139 State aInitialState
;
2145 m_xDocumentHandler
->startDocument();
2147 // get the document dimensions
2149 // if the "width" and "height" attributes are missing, inkscape fakes
2150 // A4 portrait for. Let's do the same.
2151 if (!xDocElem
->hasAttribute("width"))
2152 xDocElem
->setAttribute("width", "210mm");
2153 if (!xDocElem
->hasAttribute("height"))
2154 xDocElem
->setAttribute("height", "297mm");
2156 double fViewPortWidth( pt2mm(convLength(xDocElem
->getAttribute("width"),aInitialState
,'h')) );
2157 double fViewPortHeight( pt2mm(convLength(xDocElem
->getAttribute("height"),aInitialState
,'v')) );
2160 rtl::Reference
<SvXMLAttributeList
> xAttrs( new SvXMLAttributeList() );
2161 uno::Reference
<xml::sax::XAttributeList
> xUnoAttrs( xAttrs
.get() );
2163 xAttrs
->AddAttribute( "xmlns:office", OASIS_STR
"office:1.0" );
2164 xAttrs
->AddAttribute( "xmlns:style", OASIS_STR
"style:1.0" );
2165 xAttrs
->AddAttribute( "xmlns:text", OASIS_STR
"text:1.0" );
2166 xAttrs
->AddAttribute( "xmlns:svg", OASIS_STR
"svg-compatible:1.0" );
2167 xAttrs
->AddAttribute( "xmlns:table", OASIS_STR
"table:1.0" );
2168 xAttrs
->AddAttribute( "xmlns:draw", OASIS_STR
"drawing:1.0" );
2169 xAttrs
->AddAttribute( "xmlns:fo", OASIS_STR
"xsl-fo-compatible:1.0" );
2170 xAttrs
->AddAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink");
2171 xAttrs
->AddAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/");
2172 xAttrs
->AddAttribute( "xmlns:number", OASIS_STR
"datastyle:1.0" );
2173 xAttrs
->AddAttribute( "xmlns:presentation", OASIS_STR
"presentation:1.0" );
2174 xAttrs
->AddAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML");
2175 xAttrs
->AddAttribute( "xmlns:form", OASIS_STR
"form:1.0" );
2176 xAttrs
->AddAttribute( "xmlns:script", OASIS_STR
"script:1.0" );
2177 xAttrs
->AddAttribute( "xmlns:config", OASIS_STR
"config:1.0" );
2178 xAttrs
->AddAttribute( "xmlns:dom", "http://www.w3.org/2001/xml-events");
2179 xAttrs
->AddAttribute( "xmlns:xforms", "http://www.w3.org/2002/xforms");
2180 xAttrs
->AddAttribute( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
2181 xAttrs
->AddAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2182 xAttrs
->AddAttribute( "office:version", "1.0");
2183 xAttrs
->AddAttribute( "office:mimetype", "application/vnd.oasis.opendocument.graphics");
2185 m_xDocumentHandler
->startElement( "office:document", xUnoAttrs
);
2189 m_xDocumentHandler
->startElement( "office:settings", xUnoAttrs
);
2191 xAttrs
->AddAttribute( "config:name", "ooo:view-settings");
2192 m_xDocumentHandler
->startElement( "config:config-item-set", xUnoAttrs
);
2196 xAttrs
->AddAttribute( "config:name", "VisibleAreaTop");
2197 xAttrs
->AddAttribute( "config:type", "int");
2198 m_xDocumentHandler
->startElement( "config:config-item", xUnoAttrs
);
2200 m_xDocumentHandler
->characters( "0" );
2202 m_xDocumentHandler
->endElement( "config:config-item" );
2206 xAttrs
->AddAttribute( "config:name", "VisibleAreaLeft" );
2207 xAttrs
->AddAttribute( "config:type", "int" );
2208 m_xDocumentHandler
->startElement( "config:config-item" , xUnoAttrs
);
2210 m_xDocumentHandler
->characters( "0" );
2212 m_xDocumentHandler
->endElement( "config:config-item" );
2216 xAttrs
->AddAttribute( "config:name" , "VisibleAreaWidth" );
2217 xAttrs
->AddAttribute( "config:type" , "int" );
2218 m_xDocumentHandler
->startElement( "config:config-item" , xUnoAttrs
);
2220 sal_Int64 iWidth
= sal_Int64(fViewPortWidth
);
2221 m_xDocumentHandler
->characters( OUString::number(iWidth
) );
2223 m_xDocumentHandler
->endElement( "config:config-item" );
2227 xAttrs
->AddAttribute( "config:name", "VisibleAreaHeight" );
2228 xAttrs
->AddAttribute( "config:type", "int" );
2229 m_xDocumentHandler
->startElement( "config:config-item", xUnoAttrs
);
2231 sal_Int64 iHeight
= sal_Int64(fViewPortHeight
);
2232 m_xDocumentHandler
->characters( OUString::number(iHeight
) );
2234 m_xDocumentHandler
->endElement( "config:config-item" );
2236 m_xDocumentHandler
->endElement( "config:config-item-set" );
2238 m_xDocumentHandler
->endElement( "office:settings" );
2242 m_xDocumentHandler
->startElement( "office:automatic-styles",
2245 xAttrs
->AddAttribute( "style:name", "pagelayout1");
2246 m_xDocumentHandler
->startElement( "style:page-layout", xUnoAttrs
);
2247 // TODO(Q3): this is super-ugly. In-place container come to mind.
2250 // make page viewport-width times viewport-height mm large - add
2251 // 5% border at every side
2252 xAttrs
->AddAttribute( "fo:margin-top", "0mm");
2253 xAttrs
->AddAttribute( "fo:margin-bottom", "0mm");
2254 xAttrs
->AddAttribute( "fo:margin-left", "0mm");
2255 xAttrs
->AddAttribute( "fo:margin-right", "0mm");
2256 xAttrs
->AddAttribute( "fo:page-width", OUString::number(fViewPortWidth
)+"mm");
2257 xAttrs
->AddAttribute( "fo:page-height", OUString::number(fViewPortHeight
)+"mm");
2258 xAttrs
->AddAttribute( "style:print-orientation",
2259 fViewPortWidth
> fViewPortHeight
? OUString("landscape") : OUString("portrait") );
2260 m_xDocumentHandler
->startElement( "style:page-layout-properties", xUnoAttrs
);
2261 m_xDocumentHandler
->endElement( "style:page-layout-properties" );
2262 m_xDocumentHandler
->endElement( "style:page-layout" );
2265 xAttrs
->AddAttribute( "style:name", "pagestyle1" );
2266 xAttrs
->AddAttribute( "style:family", "drawing-page" );
2267 m_xDocumentHandler
->startElement( "style:style", xUnoAttrs
);
2270 xAttrs
->AddAttribute( "draw:background-size", "border");
2271 xAttrs
->AddAttribute( "draw:fill", "none");
2272 m_xDocumentHandler
->startElement( "style:drawing-page-properties", xUnoAttrs
);
2273 m_xDocumentHandler
->endElement( "style:drawing-page-properties" );
2274 m_xDocumentHandler
->endElement( "style:style" );
2276 StatePool aStatePool
;
2278 std::vector
< uno::Reference
<xml::dom::XElement
> > aUseElementVector
;
2280 annotateStyles(aStatePool
,aStateMap
,aInitialState
,
2281 xDocElem
,m_xDocumentHandler
,aUseElementVector
);
2283 #ifdef DEBUG_FILTER_SVGREADER
2287 m_xDocumentHandler
->endElement( "office:automatic-styles" );
2290 m_xDocumentHandler
->startElement( "office:styles", xUnoAttrs
);
2291 writeOfficeStyles( aStateMap
,
2293 m_xDocumentHandler
);
2294 m_xDocumentHandler
->endElement( "office:styles" );
2297 m_xDocumentHandler
->startElement( "office:master-styles", xUnoAttrs
);
2299 xAttrs
->AddAttribute( "style:name", "Default");
2300 xAttrs
->AddAttribute( "style:page-layout-name", "pagelayout1");
2301 xAttrs
->AddAttribute( "draw:style-name", "pagestyle1");
2302 m_xDocumentHandler
->startElement( "style:master-page", xUnoAttrs
);
2303 m_xDocumentHandler
->endElement( "style:master-page" );
2305 m_xDocumentHandler
->endElement( "office:master-styles" );
2309 m_xDocumentHandler
->startElement( "office:body", xUnoAttrs
);
2310 m_xDocumentHandler
->startElement( "office:drawing", xUnoAttrs
);
2313 xAttrs
->AddAttribute( "draw:master-page-name", "Default");
2314 xAttrs
->AddAttribute( "draw:style-name", "pagestyle1");
2315 m_xDocumentHandler
->startElement("draw:page", xUnoAttrs
);
2317 // write out all shapes
2318 writeShapes(aStatePool
,
2324 m_xDocumentHandler
->endElement( "draw:page" );
2325 m_xDocumentHandler
->endElement( "office:drawing" );
2326 m_xDocumentHandler
->endElement( "office:body" );
2327 m_xDocumentHandler
->endElement( "office:document" );
2328 m_xDocumentHandler
->endDocument();
2335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */