build fix
[LibreOffice.git] / filter / source / svg / svgreader.cxx
blob377a74680037d76783b7beeed84d9b3e1c8ef9eb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "svgreader.hxx"
11 #include <xmloff/attrlist.hxx>
12 #include "gfxtypes.hxx"
13 #include "units.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>
45 #include <map>
47 #define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"
49 using namespace ::com::sun::star;
51 namespace svgi
53 enum SvgiVisitorCaller {STYLE_ANNOTATOR, SHAPE_WRITER, STYLE_WRITER};
54 namespace
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());
84 else
85 rFunc(rElem);
87 // notify children processing
88 rFunc.push();
90 if (eCaller == SHAPE_WRITER && rElem->getTagName() == "defs")
91 return;
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 ){
99 visitElements( rFunc,
100 uno::Reference<xml::dom::XElement>(
101 xChildren->item(i),
102 uno::UNO_QUERY_THROW),
103 eCaller );
107 // children processing done
108 rFunc.pop();
111 template<typename value_type> value_type square(value_type v)
113 return v*v;
116 double colorDiffSquared(const ARGBColor& rCol1, const ARGBColor& rCol2)
118 return
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
127 polygons
129 bool PolyPolygonIsMixedOpenAndClosed( const basegfx::B2DPolyPolygon& rPoly )
131 bool bRetval(false);
132 bool bOpen(false);
133 bool bClosed(false);
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() )
141 bClosed = true;
143 else
145 bOpen = true;
148 bRetval = (bClosed && bOpen);
151 return bRetval;
154 typedef std::map<OUString,std::size_t> ElementRefMapType;
156 struct AnnotatingVisitor
158 AnnotatingVisitor(StatePool& rStatePool,
159 StateMap& rStateMap,
160 const State& rInitialState,
161 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
162 std::vector< uno::Reference<xml::dom::XElement> >& rUseElementVector,
163 bool& rGradientNotFound) :
164 mnCurrStateId(0),
165 maCurrState(),
166 maParentStates(),
167 mrStates(rStatePool),
168 mrStateMap(rStateMap),
169 mxDocumentHandler(xDocumentHandler),
170 maGradientVector(),
171 maGradientStopVector(),
172 maElementVector(),
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)
183 return;
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()));
196 switch (nTagId)
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"));
208 if(xNode.is())
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));
214 else
215 aFound = maGradientIdMap.find(sValue);
217 if( aFound != maGradientIdMap.end() )
218 maGradientVector.back() = maGradientVector[aFound->second];
219 else
221 mrGradientNotFound = true;
222 maGradientVector.pop_back();
223 return;
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() );
239 break;
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"));
251 if(xNode.is())
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));
257 else
258 aFound = maGradientIdMap.find(sValue);
260 if( aFound != maGradientIdMap.end() )
261 maGradientVector.back() = maGradientVector[aFound->second];
262 else
264 mrGradientNotFound = true;
265 maGradientVector.pop_back();
266 return;
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() );
282 break;
284 case XML_USE:
286 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
287 if(xNode.is())
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();
295 if (bFound)
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;
311 break;
316 if (bSelfCycle)
318 //drop this invalid self-referencing "use" node
319 maElementIdMap.erase(aFound);
320 bFound = false;
324 if (bFound)
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;
334 double x=0.0, y=0.0;
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()));
341 switch(nAttribId)
343 case XML_ID:
344 maElementVector.push_back(xElem);
345 maElementIdMap.insert(std::make_pair(sAttributeValue,
346 maElementVector.size() - 1));
347 break;
348 case XML_X:
349 x = convLength(sAttributeValue,maCurrState,'h');
350 break;
351 case XML_Y:
352 y = convLength(sAttributeValue,maCurrState,'v');
353 break;
354 case XML_TRANSFORM:
355 break;
356 default:
357 OUString sAttributeName = xAttributes->item(i)->getNodeName();
358 xRefElem->setAttribute(sAttributeName, sAttributeValue);
359 break;
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);
375 break;
377 case XML_STOP:
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"));
388 if(xNodeColor.is())
389 parseGradientStop( maGradientStopVector.back(),
390 maGradientStopVector.size()-1,
391 XML_STOP_COLOR,
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,
402 nTokenId,
403 xAttributes->item(i)->getNodeValue() );
406 break;
408 default:
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"));
420 if(xNodeColor.is())
421 parseAttribute(XML_COLOR, xNodeColor->getNodeValue());
423 uno::Reference<xml::dom::XNode> xNodeStyle(xAttributes->getNamedItem("style"));
424 if(xNodeStyle.is())
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,
444 sAttributeValue);
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 )
488 return
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);
496 struct StopSorter
498 explicit StopSorter( const std::vector< GradientStop >& rStopVec ) :
499 mrStopVec(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 )
517 return; //easy! :-)
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
541 return;
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(
553 colorDiffSquared(
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 )
561 nMaxIndex = i-1;
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) );
584 aBuf.append( '#' );
585 if( nRed < 0x10 )
586 aBuf.append( '0' );
587 aBuf.append( sal_Int32(nRed), 16 );
588 if( nGreen < 0x10 )
589 aBuf.append( '0' );
590 aBuf.append( sal_Int32(nGreen), 16 );
591 if( nBlue < 0x10 )
592 aBuf.append( '0' );
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 )
603 switch(eAlign)
605 default:
606 case BEFORE:
607 return OUString("start");
608 case CENTER:
609 return OUString("center");
610 case AFTER:
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);
634 if( !aRes.second )
635 return false; // not written
637 ++mnCurrStateId;
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(
644 mnCurrStateId,
645 rState));
647 // find two representative stop colors (as ODF only support
648 // start&end color)
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 ?
670 OUString("axial") :
671 OUString("linear") );
673 else
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",
686 getOdfColor(
687 maGradientStopVector[
688 rState.maFillGradient.maStops[0]].maStopColor) );
689 xAttrs->AddAttribute( "draw:end-color",
690 getOdfColor(
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
700 xAttrs->Clear();
701 xAttrs->AddAttribute( "draw:name", getStyleName("svgopacity", rState.maFillGradient.mnId) );
702 if( rState.maFillGradient.meType == Gradient::LINEAR )
704 xAttrs->AddAttribute( "draw:style", "linear" );
706 else
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",
715 OUString::number(
716 maGradientStopVector[
717 rState.maFillGradient.maStops[0]].maStopColor.a*
718 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
719 xAttrs->AddAttribute( "draw:start",
720 OUString::number(
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
734 xAttrs->Clear();
736 xAttrs->AddAttribute( "style:name", getStyleName("svgparagraphstyle", mnCurrStateId) );
737 xAttrs->AddAttribute( "style:family", "paragraph" );
738 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
740 xAttrs->Clear();
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
748 xAttrs->Clear();
750 xAttrs->AddAttribute( "style:name", getStyleName("svgtextstyle", mnCurrStateId) );
751 xAttrs->AddAttribute( "style:family", "text" );
752 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
753 xAttrs->Clear();
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" );
768 xAttrs->Clear();
769 xAttrs->AddAttribute( "style:name" , getStyleName("svggraphicstyle", mnCurrStateId) );
770 xAttrs->AddAttribute( "style:family", "graphic" );
771 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
773 xAttrs->Clear();
774 // text or shape? if the former, no use in processing any
775 // graphic attributes except stroke color, ODF can do ~nothing
776 // with text shapes
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");
794 else
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" );
803 else
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)+"%" );
819 else
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)+"%" );
828 else
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));
842 else
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;
876 else
877 nStyleId = mrStates.find(maCurrState)->mnStyleId;
879 xElem->setAttribute("internal-style-ref",
880 OUString::number(
881 nStyleId)
882 +"$0");
885 void push()
887 maParentStates.push_back(maCurrState);
890 void pop()
892 maParentStates.pop_back();
895 void parseLinearGradientData( Gradient& io_rCurrGradient,
896 const sal_Int32 nGradientNumber,
897 const sal_Int32 nTokenId,
898 const OUString& sValue )
900 switch(nTokenId)
902 case XML_GRADIENTTRANSFORM:
904 OString aValueUtf8( sValue.getStr(),
905 sValue.getLength(),
906 RTL_TEXTENCODING_UTF8 );
907 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
908 break;
910 case XML_X1:
911 io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue,maCurrState,'h');
912 break;
913 case XML_X2:
914 io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue,maCurrState,'h');
915 break;
916 case XML_Y1:
917 io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue,maCurrState,'v');
918 break;
919 case XML_Y2:
920 io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue,maCurrState,'v');
921 break;
922 case XML_ID:
923 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
924 break;
925 case XML_GRADIENTUNITS:
926 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
927 io_rCurrGradient.mbBoundingBoxUnits = true;
928 else
929 io_rCurrGradient.mbBoundingBoxUnits = false;
930 break;
931 default:
932 break;
936 void parseRadialGradientData( Gradient& io_rCurrGradient,
937 const sal_Int32 nGradientNumber,
938 const sal_Int32 nTokenId,
939 const OUString& sValue )
941 switch(nTokenId)
943 case XML_GRADIENTTRANSFORM:
945 OString aValueUtf8( sValue.getStr(),
946 sValue.getLength(),
947 RTL_TEXTENCODING_UTF8 );
948 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
949 break;
951 case XML_CX:
952 io_rCurrGradient.maCoords.radial.mfCX = convLength(sValue,maCurrState,'h');
953 break;
954 case XML_CY:
955 io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue,maCurrState,'v');
956 break;
957 case XML_FX:
958 io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue,maCurrState,'h');
959 break;
960 case XML_FY:
961 io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue,maCurrState,'v');
962 break;
963 case XML_R:
964 io_rCurrGradient.maCoords.radial.mfR = convLength(sValue,maCurrState,'r');
965 break;
966 case XML_ID:
967 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
968 break;
969 case XML_GRADIENTUNITS:
970 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
971 io_rCurrGradient.mbBoundingBoxUnits = true;
972 else
973 io_rCurrGradient.mbBoundingBoxUnits = false;
974 break;
975 default:
976 break;
980 void parseGradientStop( GradientStop& io_rGradientStop,
981 const sal_Int32 nStopNumber,
982 const sal_Int32 nTokenId,
983 const OUString& sValue )
985 switch(nTokenId)
987 case XML_HREF:
989 ElementRefMapType::iterator aFound=maStopIdMap.end();
990 if ( sValue.copy(0,1) == "#" )
991 aFound = maStopIdMap.find(sValue.copy(1));
992 else
993 aFound = maStopIdMap.find(sValue);
995 if( aFound != maStopIdMap.end() )
996 io_rGradientStop = maGradientStopVector[aFound->second];
997 break;
999 case XML_ID:
1000 maStopIdMap.insert(std::make_pair(sValue,nStopNumber));
1001 break;
1002 case XML_STOP_COLOR:
1003 if( maGradientVector.empty() ||
1004 maGradientVector.back().maStops.empty() )
1005 break;
1006 parseColor( sValue.toUtf8().getStr(), maGradientStopVector[
1007 maGradientVector.back().maStops.back()].maStopColor );
1008 break;
1009 case XML_STOP_OPACITY:
1010 if( maGradientVector.empty() ||
1011 maGradientVector.back().maStops.empty() )
1012 break;
1013 parseOpacity( sValue.toUtf8().getStr(),
1014 maGradientStopVector[
1015 maGradientVector.back().maStops.back()].maStopColor );
1016 break;
1017 case XML_OFFSET:
1018 io_rGradientStop.mnStopPosition = sValue.toDouble();
1019 break;
1020 case XML_STYLE:
1021 parseStyle( sValue );
1022 break;
1023 default:
1024 break;
1028 void parseAttribute( const sal_Int32 nTokenId,
1029 const OUString& sValue )
1031 OString aValueUtf8( sValue.getStr(),
1032 sValue.getLength(),
1033 RTL_TEXTENCODING_UTF8 );
1034 switch(nTokenId)
1036 case XML_WIDTH:
1038 const double fViewPortWidth(
1039 convLength(sValue,maCurrState,'h'));
1041 maCurrState.maViewport.expand(
1042 basegfx::B2DTuple(fViewPortWidth,0.0));
1043 break;
1045 case XML_HEIGHT:
1047 const double fViewPortHeight(
1048 convLength(sValue,maCurrState,'v'));
1050 maCurrState.maViewport.expand(
1051 basegfx::B2DTuple(0.0,fViewPortHeight));
1052 break;
1054 case XML_VIEWBOX:
1056 // TODO(F1): preserveAspectRatio
1057 parseViewBox(
1058 aValueUtf8.getStr(),
1059 maCurrState.maViewBox);
1060 break;
1062 case XML_FILL_RULE:
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;
1070 break;
1072 case XML_OPACITY:
1073 if( aValueUtf8 == "inherit" )
1074 maCurrState.mnOpacity = maParentStates.back().mnOpacity;
1075 else
1076 maCurrState.mnOpacity = aValueUtf8.toDouble();
1077 break;
1078 case XML_FILL_OPACITY:
1079 if( aValueUtf8 == "inherit" )
1080 maCurrState.mnFillOpacity = maParentStates.back().mnFillOpacity;
1081 else {
1082 maCurrState.mnFillOpacity = aValueUtf8.toDouble();
1083 if( maCurrState.mnFillOpacity > 1 )
1084 maCurrState.mnFillOpacity = 1;
1086 break;
1087 case XML_STROKE_WIDTH:
1089 if( aValueUtf8 == "inherit" )
1090 maCurrState.mnStrokeWidth = maParentStates.back().mnStrokeWidth;
1091 else
1092 maCurrState.mnStrokeWidth = convLength(sValue,maCurrState,'r');
1093 break;
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;
1105 break;
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;
1117 break;
1119 case XML_STROKE_MITERLIMIT:
1121 if( aValueUtf8 == "inherit" )
1122 maCurrState.mnMiterLimit = maParentStates.back().mnMiterLimit;
1123 else
1124 maCurrState.mnMiterLimit = aValueUtf8.toDouble();
1125 break;
1127 case XML_STROKE_DASHOFFSET:
1129 if( aValueUtf8 == "inherit" )
1130 maCurrState.mnDashOffset = maParentStates.back().mnDashOffset;
1131 else
1132 maCurrState.mnDashOffset = convLength(sValue,maCurrState,'r');
1133 break;
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;
1144 else
1146 if( parseDashArray(aValueUtf8.getStr(),
1147 maCurrState.maDashArray) )
1149 maCurrState.meStrokeType = DASH;
1151 else
1153 maCurrState.meStrokeType = SOLID;
1156 break;
1158 case XML_STROKE_OPACITY:
1159 if( aValueUtf8 == "inherit" )
1160 maCurrState.mnStrokeOpacity = maParentStates.back().mnStrokeOpacity;
1161 else
1162 maCurrState.mnStrokeOpacity = aValueUtf8.toDouble();
1163 break;
1164 case XML_FILL:
1166 const State& rParent( maParentStates.back() );
1167 parsePaint( sValue,
1168 aValueUtf8.getStr(),
1169 maCurrState.meFillType,
1170 maCurrState.maFillColor,
1171 maCurrState.maFillGradient,
1172 rParent.meFillType,
1173 rParent.maFillColor,
1174 rParent.maFillGradient );
1175 break;
1177 case XML_STROKE:
1179 const State& rParent( maParentStates.back() );
1180 parsePaint( sValue,
1181 aValueUtf8.getStr(),
1182 maCurrState.meStrokeType,
1183 maCurrState.maStrokeColor,
1184 maCurrState.maStrokeGradient,
1185 rParent.meStrokeType,
1186 rParent.maStrokeColor,
1187 rParent.maStrokeGradient );
1188 break;
1190 case XML_COLOR:
1192 if( aValueUtf8 == "inherit" )
1193 maCurrState.maCurrentColor = maParentStates.back().maCurrentColor;
1194 else
1195 parseColor(aValueUtf8.getStr(), maCurrState.maCurrentColor);
1196 break;
1198 case XML_TRANSFORM:
1200 basegfx::B2DHomMatrix aTransform;
1201 parseTransform(aValueUtf8.getStr(),aTransform);
1202 maCurrState.maTransform = maCurrState.maTransform*aTransform;
1203 break;
1205 case XML_FONT_FAMILY:
1206 maCurrState.maFontFamily=sValue;
1207 break;
1208 case XML_FONT_SIZE:
1209 maCurrState.mnParentFontSize=maParentStates.back().mnFontSize;
1210 maCurrState.mnFontSize=convLength(sValue,maCurrState,'v');
1211 break;
1212 case XML_FONT_STYLE:
1213 parseFontStyle(maCurrState,sValue,aValueUtf8.getStr());
1214 break;
1215 case XML_FONT_WEIGHT:
1216 maCurrState.mnFontWeight=sValue.toDouble();
1217 break;
1218 case XML_FONT_VARIANT:
1219 parseFontVariant(maCurrState,sValue,aValueUtf8.getStr());
1220 break;
1221 case XML_TEXT_ANCHOR:
1222 parseTextAlign(maCurrState,aValueUtf8.getStr());
1223 break;
1224 case XML_STOP_COLOR:
1225 case XML_STOP_OPACITY:
1226 parseGradientStop( maGradientStopVector.back(),
1227 maGradientStopVector.size()-1,
1228 nTokenId, sValue );
1229 break;
1230 case XML_TOKEN_INVALID:
1231 SAL_INFO("svg", "unhandled token");
1232 break;
1233 default:
1234 SAL_INFO("svg", "unhandled token " << getTokenName(nTokenId));
1235 break;
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
1251 nDummyIndex=0;
1252 OUString aCurrAttrib(
1253 aCurrToken.getToken(0,':',nDummyIndex).trim());
1254 OSL_ASSERT(nDummyIndex!=-1);
1255 nDummyIndex=0;
1256 OUString aCurrValue(
1257 aCurrToken.getToken(1,':',nDummyIndex).trim());
1258 OSL_ASSERT(nDummyIndex==-1);
1260 // recurse into normal attribute parsing
1261 parseAttribute( getTokenId(aCurrAttrib),
1262 aCurrValue );
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,
1297 const char* sValue,
1298 PaintType& rType,
1299 ARGBColor& rColor,
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,
1307 false);
1308 if( strcmp(sValue,"none") == 0 )
1309 rType = NONE;
1310 else if( strcmp(sValue,"currentColor") == 0 )
1312 rType = SOLID;
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];
1334 rType = GRADIENT;
1338 else if( aColor.second )
1340 rType = SOLID;
1341 rColor = aColor.first;
1343 else
1345 rType = NONE;
1348 else
1350 rType = SOLID;
1351 parseColor(sValue,rColor);
1355 sal_Int32 mnCurrStateId;
1356 State maCurrState;
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),
1398 mnShapeNum(0)
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);
1412 OUString sStyleId(
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()));
1424 switch(nTokenId)
1426 case XML_LINE:
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()));
1437 switch(nAttribId)
1439 case XML_X1:
1440 x1= convLength(sAttributeValue,maCurrState,'h');
1441 break;
1442 case XML_X2:
1443 x2 = convLength(sAttributeValue,maCurrState,'h');
1444 break;
1445 case XML_Y1:
1446 y1 = convLength(sAttributeValue,maCurrState,'v');
1447 break;
1448 case XML_Y2:
1449 y2 = convLength(sAttributeValue,maCurrState,'v');
1450 break;
1451 default:
1452 // skip
1453 break;
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,
1465 xUnoAttrs,
1466 xElem,
1467 sStyleId,
1468 basegfx::B2DPolyPolygon(aPoly));
1471 break;
1473 case XML_POLYGON:
1474 case XML_POLYLINE:
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,
1483 xUnoAttrs,
1484 xElem,
1485 sStyleId,
1486 basegfx::B2DPolyPolygon(aPoly));
1487 break;
1489 case XML_RECT:
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()));
1501 switch(nAttribId)
1503 case XML_X:
1504 x = convLength(sAttributeValue,maCurrState,'h');
1505 break;
1506 case XML_Y:
1507 y = convLength(sAttributeValue,maCurrState,'v');
1508 break;
1509 case XML_WIDTH:
1510 width = convLength(sAttributeValue,maCurrState,'h');
1511 break;
1512 case XML_HEIGHT:
1513 height = convLength(sAttributeValue,maCurrState,'v');
1514 break;
1515 case XML_RX:
1516 rx = convLength(sAttributeValue,maCurrState,'h');
1517 bRxSeen=true;
1518 break;
1519 case XML_RY:
1520 ry = convLength(sAttributeValue,maCurrState,'v');
1521 bRySeen=true;
1522 break;
1523 default:
1524 // skip
1525 break;
1529 if ( (width > 0) && (height > 0) ) {
1530 if( bRxSeen && !bRySeen )
1531 ry = rx;
1532 else if( bRySeen && !bRxSeen )
1533 rx = ry;
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,
1541 xUnoAttrs,
1542 xElem,
1543 sStyleId,
1544 basegfx::B2DPolyPolygon(aPoly));
1546 break;
1548 case XML_PATH:
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) &&
1556 !aPoly.isClosed())
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,
1567 xUnoAttrs,
1568 xElem,
1569 sStyleId,
1570 basegfx::B2DPolyPolygon(aPoly.getB2DPolygon(i)));
1573 else
1575 writePathShape(xAttrs,
1576 xUnoAttrs,
1577 xElem,
1578 sStyleId,
1579 aPoly);
1581 break;
1583 case XML_CIRCLE:
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()));
1594 switch(nAttribId)
1596 case XML_CX:
1597 cx = convLength(sAttributeValue,maCurrState,'h');
1598 break;
1599 case XML_CY:
1600 cy = convLength(sAttributeValue,maCurrState,'v');
1601 break;
1602 case XML_R:
1603 r = convLength(sAttributeValue,maCurrState,'r');
1604 break;
1605 default:
1606 // skip
1607 break;
1611 if ( r > 0 )
1612 writeEllipseShape(xAttrs,
1613 xUnoAttrs,
1614 xElem,
1615 sStyleId,
1616 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r)));
1617 break;
1619 case XML_ELLIPSE:
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()));
1630 switch(nAttribId)
1632 case XML_CX:
1633 cx = convLength(sAttributeValue,maCurrState,'h');
1634 break;
1635 case XML_CY:
1636 cy = convLength(sAttributeValue,maCurrState,'v');
1637 break;
1638 case XML_RX:
1639 rx = convLength(sAttributeValue,maCurrState,'h');
1640 break;
1641 case XML_RY:
1642 ry = convLength(sAttributeValue,maCurrState,'v');
1643 break;
1644 default:
1645 // skip
1646 break;
1650 if ( rx > 0 && ry > 0 )
1651 writeEllipseShape(xAttrs,
1652 xUnoAttrs,
1653 xElem,
1654 sStyleId,
1655 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry)));
1656 break;
1658 case XML_IMAGE:
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()));
1669 switch(nAttribId)
1671 case XML_X:
1672 x = convLength(sAttributeValue,maCurrState,'h');
1673 break;
1674 case XML_Y:
1675 y = convLength(sAttributeValue,maCurrState,'v');
1676 break;
1677 case XML_WIDTH:
1678 width = convLength(sAttributeValue,maCurrState,'h');
1679 break;
1680 case XML_HEIGHT:
1681 height = convLength(sAttributeValue,maCurrState,'v');
1682 break;
1683 default:
1684 // skip
1685 break;
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))
1693 // apply transform
1694 x *= aScale.getX();
1695 width *= aScale.getX();
1696 y *= aScale.getY();
1697 height *= aScale.getY();
1698 x += aTranslate.getX();
1699 y += aTranslate.getY();
1700 //TODO: Rotate
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);
1710 break;
1712 case XML_TSPAN:
1713 case XML_TEXT:
1715 // collect text from all TEXT_NODE children into sText
1716 OUStringBuffer sText;
1717 visitChildren(
1718 [&sText] (xml::dom::XNode & rNode) {
1719 return sText.append(rNode.getNodeValue());
1721 xElem,
1722 xml::dom::NodeType_TEXT_NODE);
1724 // collect attributes
1725 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1726 OUString sAttributeValue;
1727 double x=0.0,y=0.0;
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()));
1733 switch(nAttribId)
1735 case XML_X:
1736 x = convLength(sAttributeValue,maCurrState,'h');
1737 break;
1738 case XML_Y:
1739 y = convLength(sAttributeValue,maCurrState,'v');
1740 break;
1741 default:
1742 // skip
1743 break;
1747 // actually export text
1748 xAttrs->Clear();
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;
1759 // apply transform
1760 x *= aScale.getX();
1761 y *= aScale.getY();
1762 x += aTranslate.getX();
1763 y += aTranslate.getY();
1764 //TODO: Rotate
1766 else {
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);
1778 xAttrs->Clear();
1779 mxDocumentHandler->startElement("draw:text-box", xUnoAttrs);
1780 xAttrs->AddAttribute( "text:style-name", "svgparagraphstyle"+sStyleId);
1781 mxDocumentHandler->startElement("text:p", xUnoAttrs);
1783 xAttrs->Clear();
1784 xAttrs->AddAttribute( "text:style-name", "svgtextstyle"+sStyleId);
1785 mxDocumentHandler->startElement("text:span", xUnoAttrs);
1787 xAttrs->Clear();
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");
1793 break;
1798 static void push()
1801 static void pop()
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)
1810 xAttrs->Clear();
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);
1818 xAttrs->Clear();
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;
1841 xAttrs->Clear();
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;
1860 xAttrs->Clear();
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,
1886 xElem,
1887 aBounds,
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(
1900 aPoly,
1901 false, // no relative coords. causes rounding errors
1902 false, // no quad bezier detection. crashes older versions.
1903 false ));
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",
1921 "0 0 "
1922 + OUString::number(
1923 basegfx::fround(pt100thmm(rShapeBounds.getWidth())) )
1924 + " "
1925 + OUString::number(
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");
1935 State maCurrState;
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);
1958 } // namespace
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);
1977 OUString sStyleId(
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 );
1994 xAttrs->Clear();
1995 xAttrs->AddAttribute( "draw:name", "dash"+sStyleId );
1996 xAttrs->AddAttribute( "draw:display-name", "dash"+sStyleId );
1997 xAttrs->AddAttribute( "draw:style", "rect" );
1998 if ( dots1>0 ) {
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" );
2003 if ( dots2>0 ) {
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 )
2015 *dots1 = 0;
2016 *dots1_length = 0;
2017 *dots2 = 0;
2018 *dots2_length = 0;
2019 *dash_distance = 0;
2021 if( maCurrState.maDashArray.empty() ) {
2022 return;
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);
2033 ++dist_count;
2036 *dots1 = 1;
2037 *dots1_length = maCurrState.maDashArray[0];
2038 int i=2;
2039 while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots1_length ) ) {
2040 ++(*dots1);
2041 i += 2;
2043 if( i<effective_dasharray_size ) {
2044 *dots2 = 1;
2045 *dots2_length = maCurrState.maDashArray[i];
2046 i+=2;
2047 while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots2_length ) ) {
2048 ++(*dots2);
2049 i += 2;
2053 SAL_INFO("svg", "SvgDashArray2Odf " << *dash_distance << " " << *dots1 << " " << *dots1_length << " " << *dots2 << " " << *dots2_length );
2055 return;
2058 static void push() {}
2059 static void pop() {}
2061 State maCurrState;
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",
2081 OUStringToOString(
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",
2090 OUStringToOString(
2091 xElem->getTagName(),
2092 RTL_TEXTENCODING_UTF8 ).getStr());
2093 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2094 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2096 OSL_TRACE(" %s=%s",
2097 OUStringToOString(
2098 xAttributes->item(i)->getNodeName(),
2099 RTL_TEXTENCODING_UTF8 ).getStr(),
2100 OUStringToOString(
2101 xAttributes->item(i)->getNodeValue(),
2102 RTL_TEXTENCODING_UTF8 ).getStr());
2106 void push() {}
2107 void pop() {}
2110 static void dumpTree( const uno::Reference<xml::dom::XElement> xElem )
2112 DumpingVisitor aVisitor;
2113 visitElements(aVisitor, xElem, STYLE_ANNOTATOR);
2115 #endif
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;
2142 // doc boilerplate
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')) );
2159 // document prolog
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 );
2187 xAttrs->Clear();
2189 m_xDocumentHandler->startElement( "office:settings", xUnoAttrs);
2191 xAttrs->AddAttribute( "config:name", "ooo:view-settings");
2192 m_xDocumentHandler->startElement( "config:config-item-set", xUnoAttrs);
2194 xAttrs->Clear();
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" );
2204 xAttrs->Clear();
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" );
2214 xAttrs->Clear();
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" );
2225 xAttrs->Clear();
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" );
2240 xAttrs->Clear();
2242 m_xDocumentHandler->startElement( "office:automatic-styles",
2243 xUnoAttrs );
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.
2248 xAttrs->Clear();
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" );
2264 xAttrs->Clear();
2265 xAttrs->AddAttribute( "style:name", "pagestyle1" );
2266 xAttrs->AddAttribute( "style:family", "drawing-page" );
2267 m_xDocumentHandler->startElement( "style:style", xUnoAttrs );
2269 xAttrs->Clear();
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;
2277 StateMap aStateMap;
2278 std::vector< uno::Reference<xml::dom::XElement> > aUseElementVector;
2280 annotateStyles(aStatePool,aStateMap,aInitialState,
2281 xDocElem,m_xDocumentHandler,aUseElementVector);
2283 #ifdef DEBUG_FILTER_SVGREADER
2284 dumpTree(xDocElem);
2285 #endif
2287 m_xDocumentHandler->endElement( "office:automatic-styles" );
2289 xAttrs->Clear();
2290 m_xDocumentHandler->startElement( "office:styles", xUnoAttrs);
2291 writeOfficeStyles( aStateMap,
2292 xDocElem,
2293 m_xDocumentHandler);
2294 m_xDocumentHandler->endElement( "office:styles" );
2297 m_xDocumentHandler->startElement( "office:master-styles", xUnoAttrs );
2298 xAttrs->Clear();
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" );
2308 xAttrs->Clear();
2309 m_xDocumentHandler->startElement( "office:body", xUnoAttrs );
2310 m_xDocumentHandler->startElement( "office:drawing", xUnoAttrs );
2312 xAttrs->Clear();
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,
2319 aStateMap,
2320 xDocElem,
2321 m_xDocumentHandler,
2322 aUseElementVector);
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();
2330 return true;
2333 } // namespace svgi
2335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */