fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / filter / source / svg / svgreader.cxx
blobe3f1af32fd7e43085560a409b2babba9f68effdb
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 <boost/bind.hpp>
46 #include <boost/unordered_set.hpp>
47 #include <map>
48 #include <string.h>
50 #define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"
52 using namespace ::com::sun::star;
54 namespace svgi
56 namespace
59 /** visits all children of the specified type with the given functor
61 template<typename Func> void visitChildren(const Func& rFunc,
62 const uno::Reference<xml::dom::XElement> xElem,
63 xml::dom::NodeType eChildType )
65 uno::Reference<xml::dom::XNodeList> xChildren( xElem->getChildNodes() );
66 const sal_Int32 nNumNodes( xChildren->getLength() );
67 for( sal_Int32 i=0; i<nNumNodes; ++i )
69 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() << "|");
70 if( xChildren->item(i)->getNodeType() == eChildType )
71 rFunc( *xChildren->item(i).get() );
75 /** Visit all elements of the given tree (in-order traversal)
77 Given functor is called for every element, and passed the
78 element's attributes, if any
80 template<typename Func> void visitElements(Func& rFunc,
81 const uno::Reference<xml::dom::XElement> xElem)
83 if( xElem->hasAttributes() )
84 rFunc(xElem,xElem->getAttributes());
85 else
86 rFunc(xElem);
88 // notify children processing
89 rFunc.push();
91 // recurse over children
92 uno::Reference<xml::dom::XNodeList> xChildren( xElem->getChildNodes() );
93 const sal_Int32 nNumNodes( xChildren->getLength() );
94 for( sal_Int32 i=0; i<nNumNodes; ++i )
96 if( xChildren->item(i)->getNodeType() == xml::dom::NodeType_ELEMENT_NODE )
97 visitElements( rFunc,
98 uno::Reference<xml::dom::XElement>(
99 xChildren->item(i),
100 uno::UNO_QUERY_THROW) );
103 // children processing done
104 rFunc.pop();
107 template<typename value_type> value_type square(value_type v)
109 return v*v;
112 double colorDiffSquared(const ARGBColor& rCol1, const ARGBColor& rCol2)
114 return
115 square(rCol1.a-rCol2.a)
116 + square(rCol1.r-rCol2.r)
117 + square(rCol1.g-rCol2.g)
118 + square(rCol1.b-rCol2.b);
121 typedef std::map<OUString,sal_Size> ElementRefMapType;
123 struct AnnotatingVisitor
125 AnnotatingVisitor(StatePool& rStatePool,
126 StateMap& rStateMap,
127 const State& rInitialState,
128 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
129 mnCurrStateId(0),
130 maCurrState(),
131 maParentStates(),
132 mrStates(rStatePool),
133 mrStateMap(rStateMap),
134 mxDocumentHandler(xDocumentHandler),
135 maGradientVector(),
136 maGradientStopVector()
138 maParentStates.push_back(rInitialState);
141 void operator()( const uno::Reference<xml::dom::XElement>& xElem)
143 const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
144 if (nTagId != XML_TEXT)
145 return;
147 maCurrState = maParentStates.back();
148 maCurrState.maTransform.identity();
149 maCurrState.maViewBox.reset();
150 // if necessary, serialize to automatic-style section
151 writeStyle(xElem,nTagId);
154 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
155 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
157 const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
158 switch (nTagId)
160 case XML_LINEARGRADIENT:
162 const sal_Int32 nNumAttrs( xAttributes->getLength() );
163 maGradientVector.push_back(Gradient(Gradient::LINEAR));
165 // do we have a reference to a parent gradient? parse
166 // that first, as it sets our defaults here (manually
167 // tracking default state on each Gradient variable is
168 // much more overhead)
169 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
170 if(xNode.is())
172 const OUString sValue(xNode->getNodeValue());
173 ElementRefMapType::iterator aFound=maGradientIdMap.end();
174 if ( sValue.copy(0,1) == "#" )
175 aFound = maGradientIdMap.find(sValue.copy(1));
176 else
177 aFound = maGradientIdMap.find(sValue);
179 if( aFound != maGradientIdMap.end() )
180 maGradientVector.back() = maGradientVector[aFound->second];
183 // do that after dereferencing, to prevent hyperlinked
184 // gradient to clobber our Id again
185 maGradientVector.back().mnId = maGradientVector.size()-1;
186 maGradientVector.back().meType = Gradient::LINEAR; // has been clobbered as well
188 for( sal_Int32 i=0; i<nNumAttrs; ++i )
190 parseLinearGradientData( maGradientVector.back(),
191 maGradientVector.size()-1,
192 getTokenId(xAttributes->item(i)->getNodeName()),
193 xAttributes->item(i)->getNodeValue() );
195 break;
197 case XML_RADIALGRADIENT:
199 const sal_Int32 nNumAttrs( xAttributes->getLength() );
200 maGradientVector.push_back(Gradient(Gradient::RADIAL));
202 // do we have a reference to a parent gradient? parse
203 // that first, as it sets our defaults here (manually
204 // tracking default state on each Gradient variable is
205 // much more overhead)
206 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
207 if(xNode.is())
209 const OUString sValue(xNode->getNodeValue());
210 ElementRefMapType::iterator aFound=maGradientIdMap.end();
211 if ( sValue.copy(0,1) == "#" )
212 aFound = maGradientIdMap.find(sValue.copy(1));
213 else
214 aFound = maGradientIdMap.find(sValue);
216 if( aFound != maGradientIdMap.end() )
217 maGradientVector.back() = maGradientVector[aFound->second];
220 // do that after dereferencing, to prevent hyperlinked
221 // gradient to clobber our Id again
222 maGradientVector.back().mnId = maGradientVector.size()-1;
223 maGradientVector.back().meType = Gradient::RADIAL; // has been clobbered as well
225 for( sal_Int32 i=0; i<nNumAttrs; ++i )
227 parseRadialGradientData( maGradientVector.back(),
228 maGradientVector.size()-1,
229 getTokenId(xAttributes->item(i)->getNodeName()),
230 xAttributes->item(i)->getNodeValue() );
232 break;
234 case XML_STOP:
236 const sal_Int32 nNumAttrs( xAttributes->getLength() );
237 maGradientStopVector.push_back(GradientStop());
238 maGradientVector.back().maStops.push_back(maGradientStopVector.size()-1);
239 for( sal_Int32 i=0; i<nNumAttrs; ++i )
241 parseGradientStop( maGradientStopVector.back(),
242 maGradientStopVector.size()-1,
243 getTokenId(xAttributes->item(i)->getNodeName()),
244 xAttributes->item(i)->getNodeValue() );
246 break;
248 default:
250 // init state. inherit defaults from parent.
251 maCurrState = maParentStates.back();
252 maCurrState.maTransform.identity();
253 maCurrState.maViewBox.reset();
255 // scan for style info
256 const sal_Int32 nNumAttrs( xAttributes->getLength() );
257 OUString sAttributeValue;
258 for( sal_Int32 i=0; i<nNumAttrs; ++i )
260 sAttributeValue = xAttributes->item(i)->getNodeValue();
261 const sal_Int32 nTokenId(
262 getTokenId(xAttributes->item(i)->getNodeName()));
263 if( XML_STYLE == nTokenId )
264 parseStyle(sAttributeValue);
265 else
266 parseAttribute(nTokenId,
267 sAttributeValue);
270 // all attributes parsed, can calc total CTM now
271 basegfx::B2DHomMatrix aLocalTransform;
272 if( !maCurrState.maViewBox.isEmpty() &&
273 maCurrState.maViewBox.getWidth() != 0.0 &&
274 maCurrState.maViewBox.getHeight() != 0.0 )
276 // transform aViewBox into viewport, keep aspect ratio
277 aLocalTransform.translate(-maCurrState.maViewBox.getMinX(),
278 -maCurrState.maViewBox.getMinY());
279 double scaleW = maCurrState.maViewport.getWidth()/maCurrState.maViewBox.getWidth();
280 double scaleH = maCurrState.maViewport.getHeight()/maCurrState.maViewBox.getHeight();
281 double scale = (scaleW < scaleH) ? scaleW : scaleH;
282 aLocalTransform.scale(scale,scale);
284 maCurrState.maCTM = maCurrState.maCTM*maCurrState.maTransform*aLocalTransform;
286 OSL_TRACE("annotateStyle - CTM is: %f %f %f %f %f %f",
287 maCurrState.maCTM.get(0,0),
288 maCurrState.maCTM.get(0,1),
289 maCurrState.maCTM.get(0,2),
290 maCurrState.maCTM.get(1,0),
291 maCurrState.maCTM.get(1,1),
292 maCurrState.maCTM.get(1,2));
294 // if necessary, serialize to automatic-style section
295 writeStyle(xElem,nTagId);
300 OUString getStyleName( const char* sPrefix, sal_Int32 nId )
302 return OUString::createFromAscii(sPrefix)+OUString::valueOf(nId);
305 bool hasGradientOpacity( const Gradient& rGradient )
307 return
308 (rGradient.maStops.size() > 1) &&
309 (maGradientStopVector[
310 rGradient.maStops[0]].maStopColor.a != 1.0 ||
311 maGradientStopVector[
312 rGradient.maStops[1]].maStopColor.a != 1.0);
315 struct StopSorter
317 explicit StopSorter( const std::vector< GradientStop >& rStopVec ) :
318 mrStopVec(rStopVec)
321 bool operator()( sal_Size rLHS, sal_Size rRHS )
323 return mrStopVec[rLHS].mnStopPosition < mrStopVec[rRHS].mnStopPosition;
326 const std::vector< GradientStop >& mrStopVec;
329 void optimizeGradientStops( Gradient& rGradient )
331 // sort for increasing stop position
332 std::sort(rGradient.maStops.begin(),rGradient.maStops.end(),
333 StopSorter(maGradientStopVector));
335 if( rGradient.maStops.size() < 3 )
336 return; //easy! :-)
338 // join similar colors
339 std::vector<sal_Size> aNewStops(1,rGradient.maStops.front());
340 for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
342 if( maGradientStopVector[rGradient.maStops[i]].maStopColor !=
343 maGradientStopVector[aNewStops.back()].maStopColor )
344 aNewStops.push_back(rGradient.maStops[i]);
347 rGradient.maStops = aNewStops;
348 if (rGradient.maStops.size() < 2)
350 return; // can't optimize further...
353 // axial gradient, maybe?
354 if( rGradient.meType == Gradient::LINEAR &&
355 rGradient.maStops.size() == 3 &&
356 maGradientStopVector[rGradient.maStops.front()].maStopColor ==
357 maGradientStopVector[rGradient.maStops.back()].maStopColor )
359 // yep - keep it at that
360 return;
363 // find out most significant color difference, and limit to
364 // those two stops around this border (metric is
365 // super-simplistic: take euclidean distance of colors, weigh
366 // with stop distance)
367 sal_Size nMaxIndex=0;
368 double fMaxDistance=0.0;
369 for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
371 const double fCurrDistance(
372 colorDiffSquared(
373 maGradientStopVector[rGradient.maStops[i-1]].maStopColor,
374 maGradientStopVector[rGradient.maStops[i]].maStopColor) *
375 (square(maGradientStopVector[rGradient.maStops[i-1]].mnStopPosition) +
376 square(maGradientStopVector[rGradient.maStops[i]].mnStopPosition)) );
378 if( fCurrDistance > fMaxDistance )
380 nMaxIndex = i-1;
381 fMaxDistance = fCurrDistance;
384 rGradient.maStops[0] = rGradient.maStops[nMaxIndex];
385 rGradient.maStops[1] = rGradient.maStops[nMaxIndex+1];
386 rGradient.maStops.erase(rGradient.maStops.begin()+2,rGradient.maStops.end());
389 sal_Int8 toByteColor( double val )
391 // TODO(Q3): duplicated from vcl::unotools
392 return sal::static_int_cast<sal_Int8>(
393 basegfx::fround(val*255.0));
396 OUString getOdfColor( const ARGBColor& rColor )
398 // TODO(Q3): duplicated from pdfimport
399 OUStringBuffer aBuf( 7 );
400 const sal_uInt8 nRed ( toByteColor(rColor.r) );
401 const sal_uInt8 nGreen( toByteColor(rColor.g) );
402 const sal_uInt8 nBlue ( toByteColor(rColor.b) );
403 aBuf.append( sal_Unicode('#') );
404 if( nRed < 0x10 )
405 aBuf.append( sal_Unicode('0') );
406 aBuf.append( sal_Int32(nRed), 16 );
407 if( nGreen < 0x10 )
408 aBuf.append( sal_Unicode('0') );
409 aBuf.append( sal_Int32(nGreen), 16 );
410 if( nBlue < 0x10 )
411 aBuf.append( sal_Unicode('0') );
412 aBuf.append( sal_Int32(nBlue), 16 );
414 // TODO(F3): respect alpha transparency (polygons etc.)
415 OSL_ASSERT(rColor.a == 1.0);
417 return aBuf.makeStringAndClear();
420 OUString getOdfAlign( TextAlign eAlign )
422 static OUString aStart("start");
423 static OUString aEnd("end");
424 // static OUString aJustify("justify");
425 static OUString aCenter("center");
426 switch(eAlign)
428 default:
429 case BEFORE:
430 return aStart;
431 case CENTER:
432 return aCenter;
433 case AFTER:
434 return aEnd;
438 bool writeStyle(State& rState, const sal_Int32 nTagId)
440 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
441 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
443 if (XML_TEXT == nTagId) {
444 rState.mbIsText = true;
445 basegfx::B2DTuple aScale, aTranslate;
446 double fRotate, fShearX;
447 if (rState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
449 rState.mnFontSize *= aScale.getX();
453 std::pair<StatePool::iterator,
454 bool> aRes = mrStates.insert(rState);
455 SAL_INFO ("svg", "size " << mrStates.size() << " id " << const_cast<State&>(*aRes.first).mnStyleId);
457 if( !aRes.second )
458 return false; // not written
460 ++mnCurrStateId;
462 // mnStyleId does not take part in hashing/comparison
463 const_cast<State&>(*aRes.first).mnStyleId = mnCurrStateId;
464 SAL_INFO ("svg", " --> " << const_cast<State&>(*aRes.first).mnStyleId);
466 mrStateMap.insert(std::make_pair(
467 mnCurrStateId,
468 rState));
470 // find two representative stop colors (as ODF only support
471 // start&end color)
472 optimizeGradientStops(rState.maFillGradient);
474 if( !mxDocumentHandler.is() )
475 return true; // cannot write style, svm import case
477 // do we have a gradient fill? then write out gradient as well
478 if( rState.meFillType == GRADIENT && rState.maFillGradient.maStops.size() > 1 )
480 // TODO(F3): ODF12 supposedly also groks svg:linear/radialGradient. But CL says: nope.
481 xAttrs->AddAttribute( "draw:name", getStyleName("svggradient", rState.maFillGradient.mnId) );
482 if( rState.maFillGradient.meType == Gradient::LINEAR )
484 // should the optimizeGradientStops method decide that
485 // this is a three-color gradient, it prolly wanted us
486 // to take axial instead
487 xAttrs->AddAttribute( "draw:style",
488 rState.maFillGradient.maStops.size() == 3 ?
489 OUString("axial") :
490 OUString("linear") );
492 else
494 xAttrs->AddAttribute( "draw:style", "ellipsoid" );
495 xAttrs->AddAttribute( "draw:cx", "50%" );
496 xAttrs->AddAttribute( "draw:cy", "50%" );
499 basegfx::B2DTuple rScale, rTranslate;
500 double rRotate, rShearX;
501 if( rState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
502 xAttrs->AddAttribute( "draw:angle",
503 OUString::valueOf(rRotate*1800.0/M_PI ) );
504 xAttrs->AddAttribute( "draw:start-color",
505 getOdfColor(
506 maGradientStopVector[
507 rState.maFillGradient.maStops[0]].maStopColor) );
508 xAttrs->AddAttribute( "draw:end-color",
509 getOdfColor(
510 maGradientStopVector[
511 rState.maFillGradient.maStops[1]].maStopColor) );
512 xAttrs->AddAttribute( "draw:border", "0%" );
513 mxDocumentHandler->startElement( "draw:gradient", xUnoAttrs );
514 mxDocumentHandler->endElement( "draw:gradient" );
516 if( hasGradientOpacity(rState.maFillGradient) )
518 // need to write out opacity style as well
519 xAttrs->Clear();
520 xAttrs->AddAttribute( "draw:name", getStyleName("svgopacity", rState.maFillGradient.mnId) );
521 if( rState.maFillGradient.meType == Gradient::LINEAR )
523 xAttrs->AddAttribute( "draw:style", "linear" );
525 else
527 xAttrs->AddAttribute( "draw:style", "ellipsoid" );
528 xAttrs->AddAttribute( "draw:cx", "50%" );
529 xAttrs->AddAttribute( "draw:cy", "50%" );
532 // modulate gradient opacity with overall fill opacity
533 xAttrs->AddAttribute( "draw:end",
534 OUString::valueOf(
535 maGradientStopVector[
536 rState.maFillGradient.maStops[0]].maStopColor.a*
537 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
538 xAttrs->AddAttribute( "draw:start",
539 OUString::valueOf(
540 maGradientStopVector[
541 rState.maFillGradient.maStops[1]].maStopColor.a*
542 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
543 xAttrs->AddAttribute( "draw:border", "0%" );
544 mxDocumentHandler->startElement( "draw:opacity", xUnoAttrs );
545 mxDocumentHandler->endElement( "draw:opacity" );
549 // serialize to automatic-style section
550 if( nTagId == XML_TEXT )
552 // write paragraph style attributes
553 xAttrs->Clear();
555 xAttrs->AddAttribute( "style:name", getStyleName("svgparagraphstyle", mnCurrStateId) );
556 xAttrs->AddAttribute( "style:family", "paragraph" );
557 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
559 xAttrs->Clear();
560 xAttrs->AddAttribute( "fo:text-align", getOdfAlign(rState.meTextAnchor));
562 mxDocumentHandler->startElement( "style:paragraph-properties", xUnoAttrs );
563 mxDocumentHandler->endElement( "style:paragraph-properties" );
564 mxDocumentHandler->endElement( "style:style" );
566 // write text style attributes
567 xAttrs->Clear();
569 xAttrs->AddAttribute( "style:name", getStyleName("svgtextstyle", mnCurrStateId) );
570 xAttrs->AddAttribute( "style:family", "text" );
571 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
572 xAttrs->Clear();
573 xAttrs->AddAttribute( "fo:font-family", rState.maFontFamily);
574 xAttrs->AddAttribute( "fo:font-size",
575 OUString::valueOf(pt2mm(rState.mnFontSize))+"mm");
576 xAttrs->AddAttribute( "fo:font-style", rState.maFontStyle);
577 xAttrs->AddAttribute( "fo:font-variant", rState.maFontVariant);
578 xAttrs->AddAttribute( "fo:font-weight",
579 OUString::valueOf(rState.mnFontWeight));
580 xAttrs->AddAttribute( "fo:color", getOdfColor(rState.maFillColor));
582 mxDocumentHandler->startElement( "style:text-properties", xUnoAttrs );
583 mxDocumentHandler->endElement( "style:text-properties" );
584 mxDocumentHandler->endElement( "style:style" );
587 xAttrs->Clear();
588 xAttrs->AddAttribute( "style:name" , getStyleName("svggraphicstyle", mnCurrStateId) );
589 xAttrs->AddAttribute( "style:family", "graphic" );
590 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
592 xAttrs->Clear();
593 // text or shape? if the former, no use in processing any
594 // graphic attributes except stroke color, ODF can do ~nothing
595 // with text shapes
596 if( nTagId == XML_TEXT )
598 //xAttrs->AddAttribute( "draw:auto-grow-height", "true");
599 xAttrs->AddAttribute( "draw:auto-grow-width", "true");
600 xAttrs->AddAttribute( "draw:textarea-horizontal-align", "left");
601 //xAttrs->AddAttribute( "draw:textarea-vertical-align", "top");
602 xAttrs->AddAttribute( "fo:min-height", "0cm");
604 xAttrs->AddAttribute( "fo:padding-top", "0cm");
605 xAttrs->AddAttribute( "fo:padding-left", "0cm");
606 xAttrs->AddAttribute( "fo:padding-right", "0cm");
607 xAttrs->AddAttribute( "fo:padding-bottom", "0cm");
609 // disable any background shape
610 xAttrs->AddAttribute( "draw:stroke", "none");
611 xAttrs->AddAttribute( "draw:fill", "none");
613 else
615 if( rState.meFillType != NONE )
617 if( rState.meFillType == GRADIENT )
619 xAttrs->AddAttribute( "draw:fill", "gradient");
620 xAttrs->AddAttribute( "draw:fill-gradient-name",
621 getStyleName("svggradient", rState.maFillGradient.mnId) );
622 if( hasGradientOpacity(rState.maFillGradient) )
624 // needs transparency gradient as well
625 xAttrs->AddAttribute( "draw:opacity-name",
626 getStyleName("svgopacity", rState.maFillGradient.mnId) );
628 else if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
629 xAttrs->AddAttribute( "draw:opacity",
630 OUString::valueOf(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+"%" );
632 else
634 xAttrs->AddAttribute( "draw:fill", "solid");
635 xAttrs->AddAttribute( "draw:fill-color", getOdfColor(rState.maFillColor));
636 if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
637 xAttrs->AddAttribute( "draw:opacity",
638 OUString::valueOf(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+"%" );
641 else
642 xAttrs->AddAttribute( "draw:fill", "none");
644 if( rState.meStrokeType == SOLID )
646 xAttrs->AddAttribute( "draw:stroke", "solid");
647 xAttrs->AddAttribute( "svg:stroke-color", getOdfColor(rState.maStrokeColor));
649 else if( rState.meStrokeType == DASH )
651 xAttrs->AddAttribute( "draw:stroke", "dash");
652 xAttrs->AddAttribute( "draw:stroke-dash", "dash"+OUString::valueOf(mnCurrStateId));
653 xAttrs->AddAttribute( "svg:stroke-color", getOdfColor(rState.maStrokeColor));
655 else
656 xAttrs->AddAttribute( "draw:stroke", "none");
658 if( maCurrState.mnStrokeWidth != 0.0 )
660 ::basegfx::B2DVector aVec(maCurrState.mnStrokeWidth,0);
661 aVec *= maCurrState.maCTM;
662 xAttrs->AddAttribute( "svg:stroke-width", OUString::valueOf( pt2mm(aVec.getLength()) )+"mm");
664 if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_MITER )
665 xAttrs->AddAttribute( "draw:stroke-linejoin", "miter");
666 else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_ROUND )
667 xAttrs->AddAttribute( "draw:stroke-linejoin", "round");
668 else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_BEVEL )
669 xAttrs->AddAttribute( "draw:stroke-linejoin", "bevel");
670 if( maCurrState.mnStrokeOpacity*maCurrState.mnOpacity != 1.0 )
671 xAttrs->AddAttribute( "svg:stroke-opacity",
672 OUString::valueOf(100.0*maCurrState.mnStrokeOpacity*maCurrState.mnOpacity)+"%");
675 mxDocumentHandler->startElement( "style:graphic-properties", xUnoAttrs );
676 mxDocumentHandler->endElement( "style:graphic-properties" );
677 mxDocumentHandler->endElement( "style:style" );
679 return true; // newly written
682 void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, const sal_Int32 nTagId)
684 SAL_INFO ("svg", "writeStyle xElem " << xElem->getTagName());
686 sal_Int32 nStyleId=0;
687 if( writeStyle(maCurrState, nTagId) )
688 nStyleId = mnCurrStateId;
689 else
690 nStyleId = mrStates.find(maCurrState)->mnStyleId;
692 xElem->setAttribute("internal-style-ref",
693 OUString::valueOf(
694 nStyleId)
695 +"$0");
698 void push()
700 maParentStates.push_back(maCurrState);
703 void pop()
705 maParentStates.pop_back();
708 void parseLinearGradientData( Gradient& io_rCurrGradient,
709 const sal_Int32 nGradientNumber,
710 const sal_Int32 nTokenId,
711 const OUString& sValue )
713 switch(nTokenId)
715 case XML_GRADIENTTRANSFORM:
717 OString aValueUtf8( sValue.getStr(),
718 sValue.getLength(),
719 RTL_TEXTENCODING_UTF8 );
720 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
721 break;
723 case XML_X1:
724 io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue,maCurrState,'h');
725 break;
726 case XML_X2:
727 io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue,maCurrState,'h');
728 break;
729 case XML_Y1:
730 io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue,maCurrState,'v');
731 break;
732 case XML_Y2:
733 io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue,maCurrState,'v');
734 break;
735 case XML_ID:
736 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
737 break;
738 case XML_GRADIENTUNITS:
739 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
740 io_rCurrGradient.mbBoundingBoxUnits = true;
741 else
742 io_rCurrGradient.mbBoundingBoxUnits = false;
743 break;
744 default:
745 break;
749 void parseRadialGradientData( Gradient& io_rCurrGradient,
750 const sal_Int32 nGradientNumber,
751 const sal_Int32 nTokenId,
752 const OUString& sValue )
754 switch(nTokenId)
756 case XML_GRADIENTTRANSFORM:
758 OString aValueUtf8( sValue.getStr(),
759 sValue.getLength(),
760 RTL_TEXTENCODING_UTF8 );
761 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
762 break;
764 case XML_CX:
765 io_rCurrGradient.maCoords.radial.mfCX = convLength(sValue,maCurrState,'h');
766 break;
767 case XML_CY:
768 io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue,maCurrState,'v');
769 break;
770 case XML_FX:
771 io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue,maCurrState,'h');
772 break;
773 case XML_FY:
774 io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue,maCurrState,'v');
775 break;
776 case XML_R:
777 io_rCurrGradient.maCoords.radial.mfR = convLength(sValue,maCurrState,'r');
778 break;
779 case XML_ID:
780 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
781 break;
782 case XML_GRADIENTUNITS:
783 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
784 io_rCurrGradient.mbBoundingBoxUnits = true;
785 else
786 io_rCurrGradient.mbBoundingBoxUnits = false;
787 break;
788 default:
789 break;
793 void parseGradientStop( GradientStop& io_rGradientStop,
794 const sal_Int32 nStopNumber,
795 const sal_Int32 nTokenId,
796 const OUString& sValue )
798 switch(nTokenId)
800 case XML_HREF:
802 ElementRefMapType::iterator aFound=maStopIdMap.end();
803 if ( sValue.copy(0,1) == "#" )
804 aFound = maStopIdMap.find(sValue.copy(1));
805 else
806 aFound = maStopIdMap.find(sValue);
808 if( aFound != maStopIdMap.end() )
809 io_rGradientStop = maGradientStopVector[aFound->second];
810 break;
812 case XML_ID:
813 maStopIdMap.insert(std::make_pair(sValue,nStopNumber));
814 break;
815 case XML_OFFSET:
816 io_rGradientStop.mnStopPosition = sValue.toDouble();
817 break;
818 case XML_STYLE:
819 parseStyle( sValue );
820 break;
821 default:
822 break;
826 void parseAttribute( const sal_Int32 nTokenId,
827 const OUString& sValue )
829 OString aValueUtf8( sValue.getStr(),
830 sValue.getLength(),
831 RTL_TEXTENCODING_UTF8 );
832 switch(nTokenId)
834 case XML_WIDTH:
836 const double fViewPortWidth(
837 convLength(sValue,maCurrState,'h'));
839 maCurrState.maViewport.expand(
840 basegfx::B2DTuple(fViewPortWidth,0.0));
841 break;
843 case XML_HEIGHT:
845 const double fViewPortHeight(
846 convLength(sValue,maCurrState,'v'));
848 maCurrState.maViewport.expand(
849 basegfx::B2DTuple(0.0,fViewPortHeight));
850 break;
852 case XML_VIEWBOX:
854 // TODO(F1): preserveAspectRatio
855 parseViewBox(
856 aValueUtf8.getStr(),
857 maCurrState.maViewBox);
858 break;
860 case XML_FILL_RULE:
862 if( aValueUtf8 == "evenodd" )
863 maCurrState.meFillRule = EVEN_ODD;
864 else if( aValueUtf8 == "nonzero" )
865 maCurrState.meFillRule = NON_ZERO;
866 else if( aValueUtf8 == "inherit" )
867 maCurrState.meFillRule = maParentStates.back().meFillRule;
868 break;
870 case XML_OPACITY:
871 if( aValueUtf8 == "inherit" )
872 maCurrState.mnOpacity = maParentStates.back().mnOpacity;
873 else
874 maCurrState.mnOpacity = aValueUtf8.toDouble();
875 break;
876 case XML_FILL_OPACITY:
877 if( aValueUtf8 == "inherit" )
878 maCurrState.mnFillOpacity = maParentStates.back().mnFillOpacity;
879 else {
880 maCurrState.mnFillOpacity = aValueUtf8.toDouble();
881 if( maCurrState.mnFillOpacity > 1 )
882 maCurrState.mnFillOpacity = 1;
884 break;
885 case XML_STROKE_WIDTH:
887 if( aValueUtf8 == "inherit" )
888 maCurrState.mnStrokeWidth = maParentStates.back().mnStrokeWidth;
889 else
890 maCurrState.mnStrokeWidth = convLength(sValue,maCurrState,'r');
891 break;
893 case XML_STROKE_LINECAP:
895 if( aValueUtf8 == "butt" )
896 maCurrState.meLineCap = BUTT;
897 else if( aValueUtf8 == "round" )
898 maCurrState.meLineCap = ROUND;
899 else if( aValueUtf8 == "square" )
900 maCurrState.meLineCap = RECT;
901 else if( aValueUtf8 == "inherit" )
902 maCurrState.meLineCap = maParentStates.back().meLineCap;
903 break;
905 case XML_STROKE_LINEJOIN:
907 if( aValueUtf8 == "miter" )
908 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_MITER;
909 else if( aValueUtf8 == "round" )
910 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_ROUND;
911 else if( aValueUtf8 == "bevel" )
912 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_BEVEL;
913 else if( aValueUtf8 == "inherit" )
914 maCurrState.meLineJoin = maParentStates.back().meLineJoin;
915 break;
917 case XML_STROKE_MITERLIMIT:
919 if( aValueUtf8 == "inherit" )
920 maCurrState.mnMiterLimit = maParentStates.back().mnMiterLimit;
921 else
922 maCurrState.mnMiterLimit = aValueUtf8.toDouble();
923 break;
925 case XML_STROKE_DASHOFFSET:
927 if( aValueUtf8 == "inherit" )
928 maCurrState.mnDashOffset = maParentStates.back().mnDashOffset;
929 else
930 maCurrState.mnDashOffset = convLength(sValue,maCurrState,'r');
931 break;
933 case XML_STROKE_DASHARRAY:
935 if( aValueUtf8 == "none" )
937 maCurrState.maDashArray.clear();
938 maCurrState.meStrokeType = SOLID;
940 else if( aValueUtf8 == "inherit" )
941 maCurrState.maDashArray = maParentStates.back().maDashArray;
942 else
944 parseDashArray(aValueUtf8.getStr(),
945 maCurrState.maDashArray);
946 maCurrState.meStrokeType = DASH;
948 break;
950 case XML_STROKE_OPACITY:
951 if( aValueUtf8 == "inherit" )
952 maCurrState.mnStrokeOpacity = maParentStates.back().mnStrokeOpacity;
953 else
954 maCurrState.mnStrokeOpacity = aValueUtf8.toDouble();
955 break;
956 case XML_FILL:
958 const State& rParent( maParentStates.back() );
959 parsePaint( sValue,
960 aValueUtf8.getStr(),
961 maCurrState.meFillType,
962 maCurrState.maFillColor,
963 maCurrState.maFillGradient,
964 rParent.meFillType,
965 rParent.maFillColor,
966 rParent.maFillGradient );
967 break;
969 case XML_STROKE:
971 const State& rParent( maParentStates.back() );
972 parsePaint( sValue,
973 aValueUtf8.getStr(),
974 maCurrState.meStrokeType,
975 maCurrState.maStrokeColor,
976 maCurrState.maStrokeGradient,
977 rParent.meStrokeType,
978 rParent.maStrokeColor,
979 rParent.maStrokeGradient );
980 break;
982 case XML_COLOR:
984 if( aValueUtf8 == "inherit" )
985 maCurrState.maCurrentColor = maParentStates.back().maCurrentColor;
986 else
987 parseColor(aValueUtf8.getStr(), maCurrState.maCurrentColor);
988 break;
990 case XML_TRANSFORM:
992 basegfx::B2DHomMatrix aTransform;
993 parseTransform(aValueUtf8.getStr(),aTransform);
994 maCurrState.maTransform = maCurrState.maTransform*aTransform;
995 break;
997 case XML_FONT_FAMILY:
998 maCurrState.maFontFamily=sValue;
999 break;
1000 case XML_FONT_SIZE:
1001 maCurrState.mnFontSize=convLength(sValue,maCurrState,'v');
1002 break;
1003 case XML_FONT_STYLE:
1004 parseFontStyle(maCurrState,sValue,aValueUtf8.getStr());
1005 break;
1006 case XML_FONT_WEIGHT:
1007 maCurrState.mnFontWeight=sValue.toDouble();
1008 break;
1009 case XML_FONT_VARIANT:
1010 parseFontVariant(maCurrState,sValue,aValueUtf8.getStr());
1011 break;
1012 case XML_TEXT_ANCHOR:
1013 parseTextAlign(maCurrState,aValueUtf8.getStr());
1014 break;
1015 case XML_STOP_COLOR:
1016 if( maGradientVector.empty() ||
1017 maGradientVector.back().maStops.empty() )
1018 break;
1019 parseColor( aValueUtf8.getStr(),
1020 maGradientStopVector[
1021 maGradientVector.back().maStops.back()].maStopColor );
1022 break;
1023 case XML_STOP_OPACITY:
1024 if( maGradientVector.empty() ||
1025 maGradientVector.back().maStops.empty() )
1026 break;
1027 parseOpacity( aValueUtf8.getStr(),
1028 maGradientStopVector[
1029 maGradientVector.back().maStops.back()].maStopColor );
1030 break;
1031 default:
1032 SAL_INFO("svg", "unhandled token " << getTokenName(nTokenId));
1033 break;
1037 void parseStyle( const OUString& sValue )
1039 // split individual style attributes
1040 sal_Int32 nIndex=0, nDummyIndex=0;
1041 OUString aCurrToken;
1044 aCurrToken=sValue.getToken(0,';',nIndex);
1046 if( !aCurrToken.isEmpty() )
1048 // split attrib & value
1049 nDummyIndex=0;
1050 OUString aCurrAttrib(
1051 aCurrToken.getToken(0,':',nDummyIndex).trim());
1052 OSL_ASSERT(nDummyIndex!=-1);
1053 nDummyIndex=0;
1054 OUString aCurrValue(
1055 aCurrToken.getToken(1,':',nDummyIndex).trim());
1056 OSL_ASSERT(nDummyIndex==-1);
1058 // recurse into normal attribute parsing
1059 parseAttribute( getTokenId(aCurrAttrib),
1060 aCurrValue );
1063 while( nIndex != -1 );
1066 void parseFontStyle( State& io_rInitialState,
1067 const OUString& rValue,
1068 const char* sValue )
1070 if( strcmp(sValue,"inherit") != 0 )
1071 io_rInitialState.maFontStyle = rValue;
1074 void parseFontVariant( State& io_rInitialState,
1075 const OUString& rValue,
1076 const char* sValue )
1078 if( strcmp(sValue,"inherit") != 0 )
1079 io_rInitialState.maFontVariant = rValue;
1082 void parseTextAlign( State& io_rInitialState,
1083 const char* sValue )
1085 if( strcmp(sValue,"start") == 0 )
1086 io_rInitialState.meTextAnchor = BEFORE;
1087 else if( strcmp(sValue,"middle") == 0 )
1088 io_rInitialState.meTextAnchor = CENTER;
1089 else if( strcmp(sValue,"end") == 0 )
1090 io_rInitialState.meTextAnchor = AFTER;
1091 // keep current val for sValue == "inherit"
1094 void parsePaint( const OUString& rValue,
1095 const char* sValue,
1096 PaintType& rType,
1097 ARGBColor& rColor,
1098 Gradient& rGradient,
1099 const PaintType& rInheritType,
1100 const ARGBColor& rInheritColor,
1101 const Gradient& rInheritGradient )
1103 std::pair<const char*,const char*> aPaintUri((const char*)NULL,(const char*)NULL);
1104 std::pair<ARGBColor,bool> aColor(maCurrState.maCurrentColor,
1105 false);
1106 if( strcmp(sValue,"none") == 0 )
1107 rType = NONE;
1108 else if( strcmp(sValue,"currentColor") == 0 )
1110 rType = SOLID;
1111 rColor = maCurrState.maCurrentColor;
1113 else if( strcmp(sValue,"inherit") == 0)
1115 rType = rInheritType;
1116 rColor = rInheritColor;
1117 rGradient = rInheritGradient;
1119 else if( parsePaintUri(aPaintUri,aColor,sValue) )
1121 if( aPaintUri.first != aPaintUri.second )
1123 // assuming gradient. assumption does not hold generally
1124 if( strstr(sValue,")") && rValue.getLength() > 5 )
1126 ElementRefMapType::iterator aRes;
1127 if( (aRes=maGradientIdMap.find(
1128 rValue.copy(aPaintUri.first-sValue,
1129 aPaintUri.second-aPaintUri.first))) != maGradientIdMap.end() )
1131 rGradient = maGradientVector[aRes->second];
1132 rType = GRADIENT;
1136 else if( aColor.second )
1138 rType = SOLID;
1139 rColor = aColor.first;
1141 else
1143 rType = NONE;
1146 else
1148 rType = SOLID;
1149 parseColor(sValue,rColor);
1153 sal_Int32 mnCurrStateId;
1154 State maCurrState;
1155 std::vector<State> maParentStates;
1156 StatePool& mrStates;
1157 StateMap& mrStateMap;
1158 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1159 std::vector< Gradient > maGradientVector;
1160 std::vector< GradientStop > maGradientStopVector;
1161 ElementRefMapType maGradientIdMap;
1162 ElementRefMapType maStopIdMap;
1165 /// Annotate svg styles with unique references to state pool
1166 static void annotateStyles( StatePool& rStatePool,
1167 StateMap& rStateMap,
1168 const State& rInitialState,
1169 const uno::Reference<xml::dom::XElement> xElem,
1170 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1172 AnnotatingVisitor aVisitor(rStatePool,rStateMap,rInitialState,xDocHdl);
1173 visitElements(aVisitor, xElem);
1176 struct ShapeWritingVisitor
1178 ShapeWritingVisitor(StatePool& /*rStatePool*/,
1179 StateMap& rStateMap,
1180 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1181 mrStateMap(rStateMap),
1182 mxDocumentHandler(xDocumentHandler),
1183 mnShapeNum(0)
1186 void operator()( const uno::Reference<xml::dom::XElement>& )
1190 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1191 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
1193 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1194 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1196 sal_Int32 nDummyIndex(0);
1197 OUString sStyleId(
1198 xElem->getAttribute("internal-style-ref").getToken(
1199 0,'$',nDummyIndex));
1200 StateMap::iterator pOrigState=mrStateMap.find(
1201 sStyleId.toInt32());
1203 if( pOrigState == mrStateMap.end() )
1204 return; // non-exportable element, e.g. linearGradient
1206 maCurrState = pOrigState->second;
1208 const sal_Int32 nTokenId(getTokenId(xElem->getNodeName()));
1209 switch(nTokenId)
1211 case XML_LINE:
1213 // collect attributes
1214 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1215 OUString sAttributeValue;
1216 double x1=0.0,y1=0.0,x2=0.0,y2=0.0;
1217 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1219 sAttributeValue = xAttributes->item(i)->getNodeValue();
1220 const sal_Int32 nAttribId(
1221 getTokenId(xAttributes->item(i)->getNodeName()));
1222 switch(nAttribId)
1224 case XML_X1:
1225 x1= convLength(sAttributeValue,maCurrState,'h');
1226 break;
1227 case XML_X2:
1228 x2 = convLength(sAttributeValue,maCurrState,'h');
1229 break;
1230 case XML_Y1:
1231 y1 = convLength(sAttributeValue,maCurrState,'v');
1232 break;
1233 case XML_Y2:
1234 y2 = convLength(sAttributeValue,maCurrState,'v');
1235 break;
1236 default:
1237 // skip
1238 break;
1242 if ( x1 != x2 || y1 != y2 ) {
1243 OUString sLinePath = "M"+OUString::valueOf(x1)+","
1244 +OUString::valueOf(y1)+"L"+OUString::valueOf(x2)+","
1245 +OUString::valueOf(y2);
1246 basegfx::B2DPolyPolygon aPoly;
1247 basegfx::tools::importFromSvgD(aPoly, sLinePath);
1249 writePathShape(xAttrs,
1250 xUnoAttrs,
1251 xElem,
1252 sStyleId,
1253 basegfx::B2DPolyPolygon(aPoly));
1256 break;
1258 case XML_POLYGON:
1259 case XML_POLYLINE:
1261 OUString sPoints = xElem->hasAttribute("points") ? xElem->getAttribute("points") : "";
1262 basegfx::B2DPolygon aPoly;
1263 basegfx::tools::importFromSvgPoints(aPoly, sPoints);
1264 if( nTokenId == XML_POLYGON || maCurrState.meFillType != NONE )
1265 aPoly.setClosed(true);
1267 writePathShape(xAttrs,
1268 xUnoAttrs,
1269 xElem,
1270 sStyleId,
1271 basegfx::B2DPolyPolygon(aPoly));
1272 break;
1274 case XML_RECT:
1276 // collect attributes
1277 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1278 OUString sAttributeValue;
1279 bool bRxSeen=false, bRySeen=false;
1280 double x=0.0,y=0.0,width=0.0,height=0.0,rx=0.0,ry=0.0;
1281 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1283 sAttributeValue = xAttributes->item(i)->getNodeValue();
1284 const sal_Int32 nAttribId(
1285 getTokenId(xAttributes->item(i)->getNodeName()));
1286 switch(nAttribId)
1288 case XML_X:
1289 x = convLength(sAttributeValue,maCurrState,'h');
1290 break;
1291 case XML_Y:
1292 y = convLength(sAttributeValue,maCurrState,'v');
1293 break;
1294 case XML_WIDTH:
1295 width = convLength(sAttributeValue,maCurrState,'h');
1296 break;
1297 case XML_HEIGHT:
1298 height = convLength(sAttributeValue,maCurrState,'v');
1299 break;
1300 case XML_RX:
1301 rx = convLength(sAttributeValue,maCurrState,'h');
1302 bRxSeen=true;
1303 break;
1304 case XML_RY:
1305 ry = convLength(sAttributeValue,maCurrState,'v');
1306 bRySeen=true;
1307 break;
1308 default:
1309 // skip
1310 break;
1314 if ( (width > 0) && (height > 0) ) {
1315 if( bRxSeen && !bRySeen )
1316 ry = rx;
1317 else if( bRySeen && !bRxSeen )
1318 rx = ry;
1320 basegfx::B2DPolygon aPoly;
1321 aPoly = basegfx::tools::createPolygonFromRect(
1322 basegfx::B2DRange(x,y,x+width,y+height),
1323 rx/(0.5*width), ry/(0.5*height) );
1325 writePathShape(xAttrs,
1326 xUnoAttrs,
1327 xElem,
1328 sStyleId,
1329 basegfx::B2DPolyPolygon(aPoly));
1331 break;
1333 case XML_PATH:
1335 OUString sPath = xElem->hasAttribute("d") ? xElem->getAttribute("d") : "";
1336 basegfx::B2DPolyPolygon aPoly;
1337 basegfx::tools::importFromSvgD(aPoly, sPath);
1339 writePathShape(xAttrs,
1340 xUnoAttrs,
1341 xElem,
1342 sStyleId,
1343 aPoly);
1344 break;
1346 case XML_CIRCLE:
1348 // collect attributes
1349 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1350 OUString sAttributeValue;
1351 double cx=0.0,cy=0.0,r=0.0;
1352 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1354 sAttributeValue = xAttributes->item(i)->getNodeValue();
1355 const sal_Int32 nAttribId(
1356 getTokenId(xAttributes->item(i)->getNodeName()));
1357 switch(nAttribId)
1359 case XML_CX:
1360 cx = convLength(sAttributeValue,maCurrState,'h');
1361 break;
1362 case XML_CY:
1363 cy = convLength(sAttributeValue,maCurrState,'v');
1364 break;
1365 case XML_R:
1366 r = convLength(sAttributeValue,maCurrState,'r');
1367 default:
1368 // skip
1369 break;
1373 if ( r > 0 )
1374 writeEllipseShape(xAttrs,
1375 xUnoAttrs,
1376 xElem,
1377 sStyleId,
1378 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r)));
1379 break;
1381 case XML_ELLIPSE:
1383 // collect attributes
1384 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1385 OUString sAttributeValue;
1386 double cx=0.0,cy=0.0,rx=0.0, ry=0.0;
1387 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1389 sAttributeValue = xAttributes->item(i)->getNodeValue();
1390 const sal_Int32 nAttribId(
1391 getTokenId(xAttributes->item(i)->getNodeName()));
1392 switch(nAttribId)
1394 case XML_CX:
1395 cx = convLength(sAttributeValue,maCurrState,'h');
1396 break;
1397 case XML_CY:
1398 cy = convLength(sAttributeValue,maCurrState,'v');
1399 break;
1400 case XML_RX:
1401 rx = convLength(sAttributeValue,maCurrState,'h');
1402 break;
1403 case XML_RY:
1404 ry = convLength(sAttributeValue,maCurrState,'v');
1405 default:
1406 // skip
1407 break;
1411 if ( rx > 0 && ry > 0 )
1412 writeEllipseShape(xAttrs,
1413 xUnoAttrs,
1414 xElem,
1415 sStyleId,
1416 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry)));
1417 break;
1419 case XML_IMAGE:
1421 // collect attributes
1422 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1423 OUString sAttributeValue;
1424 double x=0.0, y=0.0, width=0.0, height=0.0;
1425 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1427 sAttributeValue = xAttributes->item(i)->getNodeValue();
1428 const sal_Int32 nAttribId(
1429 getTokenId(xAttributes->item(i)->getNodeName()));
1430 switch(nAttribId)
1432 case XML_X:
1433 x = convLength(sAttributeValue,maCurrState,'h');
1434 break;
1435 case XML_Y:
1436 y = convLength(sAttributeValue,maCurrState,'v');
1437 break;
1438 case XML_WIDTH:
1439 width = convLength(sAttributeValue,maCurrState,'h');
1440 break;
1441 case XML_HEIGHT:
1442 height = convLength(sAttributeValue,maCurrState,'v');
1443 break;
1444 default:
1445 // skip
1446 break;
1449 // extract basic transformations out of CTM
1450 basegfx::B2DTuple aScale, aTranslate;
1451 double fRotate, fShearX;
1452 if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
1454 // apply transform
1455 x *= aScale.getX();
1456 width *= aScale.getX();
1457 y *= aScale.getY();
1458 height *= aScale.getY();
1459 x += aTranslate.getX();
1460 y += aTranslate.getY();
1461 //TODO: Rotate
1464 OUString sValue = xElem->hasAttribute("href") ? xElem->getAttribute("href") : "";
1465 OString aValueUtf8( sValue.getStr(), sValue.getLength(), RTL_TEXTENCODING_UTF8 );
1466 std::string sLinkValue;
1467 parseXlinkHref(aValueUtf8.getStr(), sLinkValue);
1469 if (!sLinkValue.empty())
1470 writeBinaryData(xAttrs, xUnoAttrs, xElem, basegfx::B2DRange(x,y,x+width,y+height), sLinkValue);
1471 break;
1473 case XML_TEXT:
1475 // collect text from all TEXT_NODE children into sText
1476 OUStringBuffer sText;
1477 visitChildren(boost::bind(
1478 (OUStringBuffer& (OUStringBuffer::*)(const OUString& str))&OUStringBuffer::append,
1479 boost::ref(sText),
1480 boost::bind(&xml::dom::XNode::getNodeValue,
1481 _1)),
1482 xElem,
1483 xml::dom::NodeType_TEXT_NODE);
1485 // collect attributes
1486 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1487 OUString sAttributeValue;
1488 double x=0.0,y=0.0;
1489 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1491 sAttributeValue = xAttributes->item(i)->getNodeValue();
1492 const sal_Int32 nAttribId(
1493 getTokenId(xAttributes->item(i)->getNodeName()));
1494 switch(nAttribId)
1496 case XML_X:
1497 x = convLength(sAttributeValue,maCurrState,'h');
1498 break;
1499 case XML_Y:
1500 y = convLength(sAttributeValue,maCurrState,'v');
1501 break;
1502 default:
1503 // skip
1504 break;
1508 // actually export text
1509 xAttrs->Clear();
1512 // extract basic transformations out of CTM
1513 basegfx::B2DTuple aScale, aTranslate;
1514 double fRotate, fShearX;
1515 if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
1517 // some heuristic attempts to have text output
1518 // baseline-relative
1519 y -= 2.0*maCurrState.mnFontSize/aScale.getX()/3.0;
1520 // apply transform
1521 x *= aScale.getX();
1522 y *= aScale.getY();
1523 x += aTranslate.getX();
1524 y += aTranslate.getY();
1525 //TODO: Rotate
1527 else {
1528 // some heuristic attempts to have text output
1529 // baseline-relative
1530 y -= 2.0*maCurrState.mnFontSize/3.0;
1533 xAttrs->AddAttribute( "svg:x", OUString::valueOf(pt2mm(x))+"mm");
1534 xAttrs->AddAttribute( "svg:y", OUString::valueOf(pt2mm(y))+"mm");
1535 xAttrs->AddAttribute( "draw:style-name", "svggraphicstyle"+sStyleId );
1537 mxDocumentHandler->startElement("draw:frame", xUnoAttrs);
1539 xAttrs->Clear();
1540 mxDocumentHandler->startElement("draw:text-box", xUnoAttrs);
1541 xAttrs->AddAttribute( "text:style-name", "svgparagraphstyle"+sStyleId);
1542 mxDocumentHandler->startElement("text:p", xUnoAttrs);
1544 xAttrs->Clear();
1545 xAttrs->AddAttribute( "text:style-name", "svgtextstyle"+sStyleId);
1546 mxDocumentHandler->startElement("text:span", xUnoAttrs);
1548 xAttrs->Clear();
1549 mxDocumentHandler->characters(sText.makeStringAndClear());
1550 mxDocumentHandler->endElement("text:span");
1551 mxDocumentHandler->endElement("text:p");
1552 mxDocumentHandler->endElement("draw:text-box");
1553 mxDocumentHandler->endElement("draw:frame");
1554 break;
1559 void push()
1562 void pop()
1565 void writeBinaryData( rtl::Reference<SvXMLAttributeList>& xAttrs,
1566 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1567 const uno::Reference<xml::dom::XElement>& /* xElem */,
1568 const basegfx::B2DRange& rShapeBounds,
1569 const std::string& data)
1571 xAttrs->Clear();
1572 xAttrs->AddAttribute( "svg:x", OUString::valueOf(pt2mm(rShapeBounds.getMinX()))+"mm");
1573 xAttrs->AddAttribute( "svg:y", OUString::valueOf(pt2mm(rShapeBounds.getMinY()))+"mm");
1574 xAttrs->AddAttribute( "svg:width", OUString::valueOf(pt2mm(rShapeBounds.getWidth()))+"mm");
1575 xAttrs->AddAttribute( "svg:height", OUString::valueOf(pt2mm(rShapeBounds.getHeight()))+"mm");
1577 mxDocumentHandler->startElement("draw:frame", xUnoAttrs);
1579 xAttrs->Clear();
1580 mxDocumentHandler->startElement("draw:image", xUnoAttrs);
1582 mxDocumentHandler->startElement("office:binary-data", xUnoAttrs);
1584 mxDocumentHandler->characters(OUString::createFromAscii(data.c_str()));
1586 mxDocumentHandler->endElement("office:binary-data");
1588 mxDocumentHandler->endElement("draw:image");
1590 mxDocumentHandler->endElement("draw:frame");
1594 void writeTransformAttribute(const basegfx::B2DHomMatrix rMatrix, rtl::Reference<SvXMLAttributeList>& xAttrs)
1596 basegfx::B2DTuple rScale, rTranslate;
1597 double rRotate, rShearX;
1598 OUString sTransformValue;
1599 if (!rMatrix.decompose(rScale, rTranslate, rRotate, rShearX))
1600 return;
1601 if (rScale.getX() != 1.0 || rScale.getY() != 1.0)
1602 sTransformValue += "scale("+OUString::valueOf(rScale.getX())+" "
1603 +OUString::valueOf(rScale.getY())+") ";
1604 if (rTranslate.getX() != 0.0f || rTranslate.getY() != 0.0f)
1605 sTransformValue += "translate("+OUString::valueOf(rTranslate.getX()/100.0f)+"mm "
1606 +OUString::valueOf(rTranslate.getY()/100.0f)+"mm) ";
1607 if (rRotate != 0.0f)
1608 sTransformValue += "rotate("+OUString::valueOf(rRotate)+") ";
1610 if (rShearX != 0.0f)
1611 sTransformValue += "skewX("+OUString::valueOf(rShearX)+") ";
1612 if (sTransformValue.isEmpty())
1613 return;
1614 xAttrs->AddAttribute( "draw:transform", sTransformValue);
1617 void writeEllipseShape( rtl::Reference<SvXMLAttributeList>& xAttrs,
1618 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1619 const uno::Reference<xml::dom::XElement>& xElem,
1620 const OUString& rStyleId,
1621 const basegfx::B2DEllipse& rEllipse)
1623 State aState = maCurrState;
1625 xAttrs->Clear();
1627 basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(rEllipse.getB2DEllipseCenter(),
1628 rEllipse.getB2DEllipseRadius().getX(), rEllipse.getB2DEllipseRadius().getY());
1629 writePathShape(xAttrs, xUnoAttrs, xElem, rStyleId, basegfx::B2DPolyPolygon(aPoly));
1633 void writePathShape( rtl::Reference<SvXMLAttributeList>& xAttrs,
1634 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1635 const uno::Reference<xml::dom::XElement>& xElem,
1636 const OUString& rStyleId,
1637 const basegfx::B2DPolyPolygon& rPoly )
1639 // we might need to split up polypolygon into multiple path
1640 // shapes (e.g. when emulating line stroking)
1641 std::vector<basegfx::B2DPolyPolygon> aPolys(1,rPoly);
1642 State aState = maCurrState;
1643 OUString aStyleId(rStyleId);
1645 xAttrs->Clear();
1647 OSL_TRACE("writePath - the CTM is: %f %f %f %f %f %f",
1648 maCurrState.maCTM.get(0,0),
1649 maCurrState.maCTM.get(0,1),
1650 maCurrState.maCTM.get(0,2),
1651 maCurrState.maCTM.get(1,0),
1652 maCurrState.maCTM.get(1,1),
1653 maCurrState.maCTM.get(1,2));
1655 // TODO(F2): separate out shear, rotate etc.
1656 // apply transformation to polygon, to keep draw
1657 // import in 100th mm
1658 std::for_each(aPolys.begin(),aPolys.end(),
1659 boost::bind(&basegfx::B2DPolyPolygon::transform,
1660 _1,boost::cref(aState.maCTM)));
1662 for( sal_uInt32 i=0; i<aPolys.size(); ++i )
1664 const basegfx::B2DRange aBounds(
1665 aPolys[i].areControlPointsUsed() ?
1666 basegfx::tools::getRange(
1667 basegfx::tools::adaptiveSubdivideByAngle(aPolys[i])) :
1668 basegfx::tools::getRange(aPolys[i]));
1669 fillShapeProperties(xAttrs,
1670 xElem,
1671 aBounds,
1672 "svggraphicstyle"+aStyleId);
1674 // force path coordinates to 100th millimeter, after
1675 // putting polygon data at origin (ODF viewbox
1676 // calculations largely untested codepaths, as OOo always
1677 // writes "0 0 w h" viewboxes)
1678 basegfx::B2DHomMatrix aNormalize;
1679 aNormalize.translate(-aBounds.getMinX(),-aBounds.getMinY());
1680 aNormalize.scale(2540.0/72.0,2540.0/72.0);
1681 aPolys[i].transform(aNormalize);
1683 xAttrs->AddAttribute( "svg:d", basegfx::tools::exportToSvgD(
1684 aPolys[i],
1685 false, // no relative coords. causes rounding errors
1686 false )); // no quad bezier detection. crashes older versions.
1687 mxDocumentHandler->startElement("draw:path", xUnoAttrs);
1688 mxDocumentHandler->endElement("draw:path");
1692 void fillShapeProperties( rtl::Reference<SvXMLAttributeList>& xAttrs,
1693 const uno::Reference<xml::dom::XElement>& /* xElem */,
1694 const basegfx::B2DRange& rShapeBounds,
1695 const OUString& rStyleName )
1697 xAttrs->AddAttribute( "draw:z-index", OUString::valueOf( mnShapeNum++ ));
1698 xAttrs->AddAttribute( "draw:style-name", rStyleName);
1699 xAttrs->AddAttribute( "svg:width", OUString::valueOf(pt2mm(rShapeBounds.getWidth()))+"mm");
1700 xAttrs->AddAttribute( "svg:height", OUString::valueOf(pt2mm(rShapeBounds.getHeight()))+"mm");
1702 // OOo expects the viewbox to be in 100th of mm
1703 xAttrs->AddAttribute( "svg:viewBox",
1704 "0 0 "
1705 + OUString::valueOf(
1706 basegfx::fround(pt100thmm(rShapeBounds.getWidth())) )
1707 + " "
1708 + OUString::valueOf(
1709 basegfx::fround(pt100thmm(rShapeBounds.getHeight())) ));
1711 // TODO(F1): decompose transformation in calling code, and use
1712 // transform attribute here
1713 // writeTranslate(maCurrState.maCTM, xAttrs);
1714 xAttrs->AddAttribute( "svg:x", OUString::valueOf(pt2mm(rShapeBounds.getMinX()))+"mm");
1715 xAttrs->AddAttribute( "svg:y", OUString::valueOf(pt2mm(rShapeBounds.getMinY()))+"mm");
1718 State maCurrState;
1719 StateMap& mrStateMap;
1720 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1721 sal_Int32 mnShapeNum;
1724 /// Write out shapes from DOM tree
1725 static void writeShapes( StatePool& rStatePool,
1726 StateMap& rStateMap,
1727 const uno::Reference<xml::dom::XElement> xElem,
1728 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1730 ShapeWritingVisitor aVisitor(rStatePool,rStateMap,xDocHdl);
1731 visitElements(aVisitor, xElem);
1734 } // namespace
1736 struct OfficeStylesWritingVisitor
1738 OfficeStylesWritingVisitor( StateMap& rStateMap,
1739 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1740 mrStateMap(rStateMap),
1741 mxDocumentHandler(xDocumentHandler)
1743 void operator()( const uno::Reference<xml::dom::XElement>& /*xElem*/ )
1746 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1747 const uno::Reference<xml::dom::XNamedNodeMap>& /*xAttributes*/ )
1749 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1750 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1752 sal_Int32 nDummyIndex(0);
1753 OUString sStyleId(
1754 xElem->getAttribute("internal-style-ref").getToken(
1755 0,'$',nDummyIndex));
1756 StateMap::iterator pOrigState=mrStateMap.find(
1757 sStyleId.toInt32());
1759 if( pOrigState == mrStateMap.end() )
1760 return; // non-exportable element, e.g. linearGradient
1762 maCurrState = pOrigState->second;
1764 if( maCurrState.meStrokeType == DASH )
1766 sal_Int32 dots1, dots2;
1767 double dots1_length, dots2_length, dash_distance;
1768 SvgDashArray2Odf( &dots1, &dots1_length, &dots2, &dots2_length, &dash_distance );
1770 xAttrs->Clear();
1771 xAttrs->AddAttribute( "draw:name", "dash"+sStyleId );
1772 xAttrs->AddAttribute( "draw:display-name", "dash"+sStyleId );
1773 xAttrs->AddAttribute( "draw:style", "rect" );
1774 if ( dots1>0 ) {
1775 xAttrs->AddAttribute( "draw:dots1", OUString::valueOf(dots1) );
1776 xAttrs->AddAttribute( "draw:dots1-length", OUString::valueOf(pt2mm(convLength( OUString::valueOf(dots1_length), maCurrState, 'h' )))+"mm" );
1778 xAttrs->AddAttribute( "draw:distance", OUString::valueOf(pt2mm(convLength( OUString::valueOf(dash_distance), maCurrState, 'h' )))+"mm" );
1779 if ( dots2>0 ) {
1780 xAttrs->AddAttribute( "draw:dots2", OUString::valueOf(dots2) );
1781 xAttrs->AddAttribute( "draw:dots2-length", OUString::valueOf(pt2mm(convLength( OUString::valueOf(dots2_length), maCurrState, 'h' )))+"mm" );
1784 mxDocumentHandler->startElement( "draw:stroke-dash", xUnoAttrs);
1785 mxDocumentHandler->endElement( "draw:stroke-dash" );
1789 void SvgDashArray2Odf( sal_Int32 *dots1, double *dots1_length, sal_Int32 *dots2, double *dots2_length, double *dash_distance )
1791 *dots1 = 0;
1792 *dots1_length = 0;
1793 *dots2 = 0;
1794 *dots2_length = 0;
1795 *dash_distance = 0;
1797 if( maCurrState.maDashArray.size() == 0 ) {
1798 return;
1801 double effective_dasharray_size = maCurrState.maDashArray.size();
1802 if( maCurrState.maDashArray.size() % 2 == 1 )
1803 effective_dasharray_size = maCurrState.maDashArray.size()*2;
1805 *dash_distance = maCurrState.maDashArray[1%maCurrState.maDashArray.size()];
1806 sal_Int32 dist_count = 1;
1807 for( int i=3; i<effective_dasharray_size; i+=2 ) {
1808 *dash_distance = ((dist_count * *dash_distance) + maCurrState.maDashArray[i%maCurrState.maDashArray.size()])/(dist_count+1);
1809 ++dist_count;
1812 *dots1 = 1;
1813 *dots1_length = maCurrState.maDashArray[0];
1814 int i=2;
1815 while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots1_length ) ) {
1816 ++(*dots1);
1817 i += 2;
1819 if( i<effective_dasharray_size ) {
1820 *dots2 = 1;
1821 *dots2_length = maCurrState.maDashArray[i];
1822 i+=2;
1823 while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots2_length ) ) {
1824 ++(*dots2);
1825 i += 2;
1829 SAL_INFO("svg", "SvgDashArray2Odf " << *dash_distance << " " << *dots1 << " " << *dots1_length << " " << *dots2 << " " << *dots2_length );
1831 return;
1834 void push() {}
1835 void pop() {}
1837 State maCurrState;
1838 StateMap& mrStateMap;
1839 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1842 static void writeOfficeStyles( StateMap& rStateMap,
1843 const uno::Reference<xml::dom::XElement> xElem,
1844 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1846 OfficeStylesWritingVisitor aVisitor( rStateMap, xDocHdl );
1847 visitElements( aVisitor, xElem );
1850 #if OSL_DEBUG_LEVEL > 2
1851 struct DumpingVisitor
1853 void operator()( const uno::Reference<xml::dom::XElement>& xElem )
1855 OSL_TRACE("name: %s",
1856 OUStringToOString(
1857 xElem->getTagName(),
1858 RTL_TEXTENCODING_UTF8 ).getStr());
1861 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1862 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
1864 OSL_TRACE("name: %s",
1865 OUStringToOString(
1866 xElem->getTagName(),
1867 RTL_TEXTENCODING_UTF8 ).getStr());
1868 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1869 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1871 OSL_TRACE(" %s=%s",
1872 OUStringToOString(
1873 xAttributes->item(i)->getNodeName(),
1874 RTL_TEXTENCODING_UTF8 ).getStr(),
1875 OUStringToOString(
1876 xAttributes->item(i)->getNodeValue(),
1877 RTL_TEXTENCODING_UTF8 ).getStr());
1881 void push() {}
1882 void pop() {}
1885 static void dumpTree( const uno::Reference<xml::dom::XElement> xElem )
1887 DumpingVisitor aVisitor;
1888 visitElements(aVisitor, xElem);
1890 #endif
1893 SVGReader::SVGReader(const uno::Reference<lang::XMultiServiceFactory>& xServiceFactory,
1894 const uno::Reference<io::XInputStream>& xInputStream,
1895 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1896 m_xServiceFactory( xServiceFactory ),
1897 m_xInputStream( xInputStream ),
1898 m_xDocumentHandler( xDocumentHandler )
1902 sal_Bool SVGReader::parseAndConvert()
1904 uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getComponentContext(m_xServiceFactory)));
1906 uno::Reference<xml::dom::XDocument> xDom(
1907 xDomBuilder->parse(m_xInputStream),
1908 uno::UNO_QUERY_THROW );
1910 uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(),
1911 uno::UNO_QUERY_THROW );
1913 // the root state for svg document
1914 State aInitialState;
1916 /////////////////////////////////////////////////////////////////
1917 // doc boilerplate
1918 /////////////////////////////////////////////////////////////////
1920 m_xDocumentHandler->startDocument();
1922 // get the document dimensions
1924 // if the "width" and "height" attributes are missing, inkscape fakes
1925 // A4 portrait for. Let's do the same.
1926 if (!xDocElem->hasAttribute("width"))
1927 xDocElem->setAttribute("width", "210mm");
1928 if (!xDocElem->hasAttribute("height"))
1929 xDocElem->setAttribute("height", "297mm");
1931 double fViewPortWidth( pt2mm(convLength(xDocElem->getAttribute("width"),aInitialState,'h')) );
1932 double fViewPortHeight( pt2mm(convLength(xDocElem->getAttribute("height"),aInitialState,'v')) );
1934 // document prolog
1935 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1936 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1938 xAttrs->AddAttribute( "xmlns:office", OASIS_STR "office:1.0" );
1939 xAttrs->AddAttribute( "xmlns:style", OASIS_STR "style:1.0" );
1940 xAttrs->AddAttribute( "xmlns:text", OASIS_STR "text:1.0" );
1941 xAttrs->AddAttribute( "xmlns:svg", OASIS_STR "svg-compatible:1.0" );
1942 xAttrs->AddAttribute( "xmlns:table", OASIS_STR "table:1.0" );
1943 xAttrs->AddAttribute( "xmlns:draw", OASIS_STR "drawing:1.0" );
1944 xAttrs->AddAttribute( "xmlns:fo", OASIS_STR "xsl-fo-compatible:1.0" );
1945 xAttrs->AddAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink");
1946 xAttrs->AddAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/");
1947 xAttrs->AddAttribute( "xmlns:number", OASIS_STR "datastyle:1.0" );
1948 xAttrs->AddAttribute( "xmlns:presentation", OASIS_STR "presentation:1.0" );
1949 xAttrs->AddAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML");
1950 xAttrs->AddAttribute( "xmlns:form", OASIS_STR "form:1.0" );
1951 xAttrs->AddAttribute( "xmlns:script", OASIS_STR "script:1.0" );
1952 xAttrs->AddAttribute( "xmlns:dom", "http://www.w3.org/2001/xml-events");
1953 xAttrs->AddAttribute( "xmlns:xforms", "http://www.w3.org/2002/xforms");
1954 xAttrs->AddAttribute( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
1955 xAttrs->AddAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1956 xAttrs->AddAttribute( "office:version", "1.0");
1957 xAttrs->AddAttribute( "office:mimetype", "application/vnd.oasis.opendocument.graphics");
1959 m_xDocumentHandler->startElement( "office:document", xUnoAttrs );
1961 xAttrs->Clear();
1963 m_xDocumentHandler->startElement( "office:settings", xUnoAttrs);
1965 xAttrs->AddAttribute( "config:name", "ooo:view-settings");
1966 m_xDocumentHandler->startElement( "config:config-item-set", xUnoAttrs);
1968 xAttrs->Clear();
1970 xAttrs->AddAttribute( "config:name", "VisibleAreaTop");
1971 xAttrs->AddAttribute( "config:type", "int");
1972 m_xDocumentHandler->startElement( "config:config-item", xUnoAttrs);
1974 m_xDocumentHandler->characters( "0" );
1976 m_xDocumentHandler->endElement( "config:config-item" );
1978 xAttrs->Clear();
1980 xAttrs->AddAttribute( "config:name", "VisibleAreaLeft" );
1981 xAttrs->AddAttribute( "config:type", "int" );
1982 m_xDocumentHandler->startElement( "config:config-item" , xUnoAttrs);
1984 m_xDocumentHandler->characters( "0" );
1986 m_xDocumentHandler->endElement( "config:config-item" );
1988 xAttrs->Clear();
1990 xAttrs->AddAttribute( "config:name" , "VisibleAreaWidth" );
1991 xAttrs->AddAttribute( "config:type" , "int" );
1992 m_xDocumentHandler->startElement( "config:config-item" , xUnoAttrs);
1994 sal_Int64 iWidth = sal_Int64(fViewPortWidth);
1995 m_xDocumentHandler->characters( OUString::valueOf(iWidth) );
1997 m_xDocumentHandler->endElement( "config:config-item" );
1999 xAttrs->Clear();
2001 xAttrs->AddAttribute( "config:name", "VisibleAreaHeight" );
2002 xAttrs->AddAttribute( "config:type", "int" );
2003 m_xDocumentHandler->startElement( "config:config-item", xUnoAttrs);
2005 sal_Int64 iHeight = sal_Int64(fViewPortHeight);
2006 m_xDocumentHandler->characters( OUString::valueOf(iHeight) );
2008 m_xDocumentHandler->endElement( "config:config-item" );
2010 m_xDocumentHandler->endElement( "config:config-item-set" );
2012 m_xDocumentHandler->endElement( "office:settings" );
2014 xAttrs->Clear();
2016 m_xDocumentHandler->startElement( "office:automatic-styles",
2017 xUnoAttrs );
2019 xAttrs->AddAttribute( "style:name", "pagelayout1");
2020 m_xDocumentHandler->startElement( "style:page-layout", xUnoAttrs );
2021 // TODO(Q3): this is super-ugly. In-place container come to mind.
2022 xAttrs->Clear();
2024 // make page viewport-width times viewport-height mm large - add
2025 // 5% border at every side
2026 xAttrs->AddAttribute( "fo:margin-top", "0mm");
2027 xAttrs->AddAttribute( "fo:margin-bottom", "0mm");
2028 xAttrs->AddAttribute( "fo:margin-left", "0mm");
2029 xAttrs->AddAttribute( "fo:margin-right", "0mm");
2030 xAttrs->AddAttribute( "fo:page-width", OUString::valueOf(fViewPortWidth)+"mm");
2031 xAttrs->AddAttribute( "fo:page-height", OUString::valueOf(fViewPortHeight)+"mm");
2032 xAttrs->AddAttribute( "style:print-orientation",
2033 fViewPortWidth > fViewPortHeight ? OUString("landscape") : OUString("portrait") );
2034 m_xDocumentHandler->startElement( "style:page-layout-properties", xUnoAttrs );
2035 m_xDocumentHandler->endElement( "style:page-layout-properties" );
2036 m_xDocumentHandler->endElement( "style:page-layout" );
2038 xAttrs->Clear();
2039 xAttrs->AddAttribute( "style:name", "pagestyle1" );
2040 xAttrs->AddAttribute( "style:family", "drawing-page" );
2041 m_xDocumentHandler->startElement( "style:style", xUnoAttrs );
2043 xAttrs->Clear();
2044 xAttrs->AddAttribute( "draw:background-size", "border");
2045 xAttrs->AddAttribute( "draw:fill", "none");
2046 m_xDocumentHandler->startElement( "style:drawing-page-properties", xUnoAttrs );
2047 m_xDocumentHandler->endElement( "style:drawing-page-properties" );
2048 m_xDocumentHandler->endElement( "style:style" );
2050 StatePool aStatePool;
2051 StateMap aStateMap;
2052 annotateStyles(aStatePool,aStateMap,aInitialState,
2053 xDocElem,m_xDocumentHandler);
2055 #if OSL_DEBUG_LEVEL > 2
2056 dumpTree(xDocElem);
2057 #endif
2059 m_xDocumentHandler->endElement( "office:automatic-styles" );
2061 ////////////////////////////////////////////////////////////////////
2063 xAttrs->Clear();
2064 m_xDocumentHandler->startElement( "office:styles", xUnoAttrs);
2065 writeOfficeStyles( aStateMap,
2066 xDocElem,
2067 m_xDocumentHandler);
2068 m_xDocumentHandler->endElement( "office:styles" );
2070 ////////////////////////////////////////////////////////////////////
2072 m_xDocumentHandler->startElement( "office:master-styles", xUnoAttrs );
2073 xAttrs->Clear();
2074 xAttrs->AddAttribute( "style:name", "Default");
2075 xAttrs->AddAttribute( "style:page-layout-name", "pagelayout1");
2076 xAttrs->AddAttribute( "draw:style-name", "pagestyle1");
2077 m_xDocumentHandler->startElement( "style:master-page", xUnoAttrs );
2078 m_xDocumentHandler->endElement( "style:master-page" );
2080 m_xDocumentHandler->endElement( "office:master-styles" );
2082 ////////////////////////////////////////////////////////////////////
2084 xAttrs->Clear();
2085 m_xDocumentHandler->startElement( "office:body", xUnoAttrs );
2086 m_xDocumentHandler->startElement( "office:drawing", xUnoAttrs );
2088 xAttrs->Clear();
2089 xAttrs->AddAttribute( "draw:master-page-name", "Default");
2090 xAttrs->AddAttribute( "draw:style-name", "pagestyle1");
2091 m_xDocumentHandler->startElement("draw:page", xUnoAttrs);
2093 // write out all shapes
2094 writeShapes(aStatePool,
2095 aStateMap,
2096 xDocElem,
2097 m_xDocumentHandler);
2099 m_xDocumentHandler->endElement( "draw:page" );
2100 m_xDocumentHandler->endElement( "office:drawing" );
2101 m_xDocumentHandler->endElement( "office:body" );
2102 m_xDocumentHandler->endElement( "office:document" );
2103 m_xDocumentHandler->endDocument();
2105 return sal_True;
2108 } // namespace svgi
2110 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */