Bump version to 5.0-14
[LibreOffice.git] / filter / source / svg / svgreader.cxx
blob4c5b9b08af52cf6963b84aa8c56aba42390bafc1
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 <map>
47 #include <string.h>
49 #define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"
51 using namespace ::com::sun::star;
53 namespace svgi
55 enum SvgiVisitorCaller {STYLE_ANNOTATOR, SHAPE_WRITER, STYLE_WRITER};
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>& rElem,
63 xml::dom::NodeType eChildType )
65 uno::Reference<xml::dom::XNodeList> xChildren( rElem->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>& rElem,
82 SvgiVisitorCaller eCaller)
84 if( rElem->hasAttributes() )
85 rFunc(rElem, rElem->getAttributes());
86 else
87 rFunc(rElem);
89 // notify children processing
90 rFunc.push();
92 // recurse over children
93 if (eCaller == SHAPE_WRITER && rElem->getTagName() == "defs") {
94 return;
96 uno::Reference<xml::dom::XNodeList> xChildren( rElem->getChildNodes() );
97 const sal_Int32 nNumNodes( xChildren->getLength() );
98 for( sal_Int32 i=0; i<nNumNodes; ++i )
100 if( xChildren->item(i)->getNodeType() == xml::dom::NodeType_ELEMENT_NODE )
101 visitElements( rFunc,
102 uno::Reference<xml::dom::XElement>(
103 xChildren->item(i),
104 uno::UNO_QUERY_THROW),
105 eCaller );
108 // children processing done
109 rFunc.pop();
112 template<typename value_type> value_type square(value_type v)
114 return v*v;
117 double colorDiffSquared(const ARGBColor& rCol1, const ARGBColor& rCol2)
119 return
120 square(rCol1.a-rCol2.a)
121 + square(rCol1.r-rCol2.r)
122 + square(rCol1.g-rCol2.g)
123 + square(rCol1.b-rCol2.b);
126 typedef std::map<OUString,sal_Size> ElementRefMapType;
128 struct AnnotatingVisitor
130 AnnotatingVisitor(StatePool& rStatePool,
131 StateMap& rStateMap,
132 const State& rInitialState,
133 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
134 mnCurrStateId(0),
135 maCurrState(),
136 maParentStates(),
137 mrStates(rStatePool),
138 mrStateMap(rStateMap),
139 mxDocumentHandler(xDocumentHandler),
140 maGradientVector(),
141 maGradientStopVector()
143 maParentStates.push_back(rInitialState);
146 void operator()( const uno::Reference<xml::dom::XElement>& xElem)
148 const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
149 if (nTagId != XML_TEXT)
150 return;
152 maCurrState = maParentStates.back();
153 maCurrState.maTransform.identity();
154 maCurrState.maViewBox.reset();
155 // if necessary, serialize to automatic-style section
156 writeStyle(xElem,nTagId);
159 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
160 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
162 const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
163 switch (nTagId)
165 case XML_LINEARGRADIENT:
167 const sal_Int32 nNumAttrs( xAttributes->getLength() );
168 maGradientVector.push_back(Gradient(Gradient::LINEAR));
170 // do we have a reference to a parent gradient? parse
171 // that first, as it sets our defaults here (manually
172 // tracking default state on each Gradient variable is
173 // much more overhead)
174 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
175 if(xNode.is())
177 const OUString sValue(xNode->getNodeValue());
178 ElementRefMapType::iterator aFound=maGradientIdMap.end();
179 if ( sValue.copy(0,1) == "#" )
180 aFound = maGradientIdMap.find(sValue.copy(1));
181 else
182 aFound = maGradientIdMap.find(sValue);
184 if( aFound != maGradientIdMap.end() )
185 maGradientVector.back() = maGradientVector[aFound->second];
188 // do that after dereferencing, to prevent hyperlinked
189 // gradient to clobber our Id again
190 maGradientVector.back().mnId = maGradientVector.size()-1;
191 maGradientVector.back().meType = Gradient::LINEAR; // has been clobbered as well
193 for( sal_Int32 i=0; i<nNumAttrs; ++i )
195 parseLinearGradientData( maGradientVector.back(),
196 maGradientVector.size()-1,
197 getTokenId(xAttributes->item(i)->getNodeName()),
198 xAttributes->item(i)->getNodeValue() );
200 break;
202 case XML_RADIALGRADIENT:
204 const sal_Int32 nNumAttrs( xAttributes->getLength() );
205 maGradientVector.push_back(Gradient(Gradient::RADIAL));
207 // do we have a reference to a parent gradient? parse
208 // that first, as it sets our defaults here (manually
209 // tracking default state on each Gradient variable is
210 // much more overhead)
211 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
212 if(xNode.is())
214 const OUString sValue(xNode->getNodeValue());
215 ElementRefMapType::iterator aFound=maGradientIdMap.end();
216 if ( sValue.copy(0,1) == "#" )
217 aFound = maGradientIdMap.find(sValue.copy(1));
218 else
219 aFound = maGradientIdMap.find(sValue);
221 if( aFound != maGradientIdMap.end() )
222 maGradientVector.back() = maGradientVector[aFound->second];
225 // do that after dereferencing, to prevent hyperlinked
226 // gradient to clobber our Id again
227 maGradientVector.back().mnId = maGradientVector.size()-1;
228 maGradientVector.back().meType = Gradient::RADIAL; // has been clobbered as well
230 for( sal_Int32 i=0; i<nNumAttrs; ++i )
232 parseRadialGradientData( maGradientVector.back(),
233 maGradientVector.size()-1,
234 getTokenId(xAttributes->item(i)->getNodeName()),
235 xAttributes->item(i)->getNodeValue() );
237 break;
239 case XML_STOP:
241 const sal_Int32 nNumAttrs( xAttributes->getLength() );
242 maGradientStopVector.push_back(GradientStop());
243 maGradientVector.back().maStops.push_back(maGradientStopVector.size()-1);
244 for( sal_Int32 i=0; i<nNumAttrs; ++i )
246 parseGradientStop( maGradientStopVector.back(),
247 maGradientStopVector.size()-1,
248 getTokenId(xAttributes->item(i)->getNodeName()),
249 xAttributes->item(i)->getNodeValue() );
251 break;
253 default:
255 // init state. inherit defaults from parent.
256 maCurrState = maParentStates.back();
257 maCurrState.maTransform.identity();
258 maCurrState.maViewBox.reset();
260 // scan for style info
261 const sal_Int32 nNumAttrs( xAttributes->getLength() );
262 OUString sAttributeValue;
263 for( sal_Int32 i=0; i<nNumAttrs; ++i )
265 sAttributeValue = xAttributes->item(i)->getNodeValue();
266 const sal_Int32 nTokenId(
267 getTokenId(xAttributes->item(i)->getNodeName()));
268 if( XML_STYLE == nTokenId )
269 parseStyle(sAttributeValue);
270 else
271 parseAttribute(nTokenId,
272 sAttributeValue);
275 // all attributes parsed, can calc total CTM now
276 basegfx::B2DHomMatrix aLocalTransform;
277 if( !maCurrState.maViewBox.isEmpty() &&
278 maCurrState.maViewBox.getWidth() != 0.0 &&
279 maCurrState.maViewBox.getHeight() != 0.0 )
281 // transform aViewBox into viewport, keep aspect ratio
282 aLocalTransform.translate(-maCurrState.maViewBox.getMinX(),
283 -maCurrState.maViewBox.getMinY());
284 double scaleW = maCurrState.maViewport.getWidth()/maCurrState.maViewBox.getWidth();
285 double scaleH = maCurrState.maViewport.getHeight()/maCurrState.maViewBox.getHeight();
286 double scale = (scaleW < scaleH) ? scaleW : scaleH;
287 aLocalTransform.scale(scale,scale);
289 maCurrState.maCTM = maCurrState.maCTM*maCurrState.maTransform*aLocalTransform;
291 OSL_TRACE("annotateStyle - CTM is: %f %f %f %f %f %f",
292 maCurrState.maCTM.get(0,0),
293 maCurrState.maCTM.get(0,1),
294 maCurrState.maCTM.get(0,2),
295 maCurrState.maCTM.get(1,0),
296 maCurrState.maCTM.get(1,1),
297 maCurrState.maCTM.get(1,2));
299 // if necessary, serialize to automatic-style section
300 writeStyle(xElem,nTagId);
305 static OUString getStyleName( const char* sPrefix, sal_Int32 nId )
307 return OUString::createFromAscii(sPrefix)+OUString::number(nId);
310 bool hasGradientOpacity( const Gradient& rGradient )
312 return
313 (rGradient.maStops.size() > 1) &&
314 (maGradientStopVector[
315 rGradient.maStops[0]].maStopColor.a != 1.0 ||
316 maGradientStopVector[
317 rGradient.maStops[1]].maStopColor.a != 1.0);
320 struct StopSorter
322 explicit StopSorter( const std::vector< GradientStop >& rStopVec ) :
323 mrStopVec(rStopVec)
326 bool operator()( sal_Size rLHS, sal_Size rRHS )
328 return mrStopVec[rLHS].mnStopPosition < mrStopVec[rRHS].mnStopPosition;
331 const std::vector< GradientStop >& mrStopVec;
334 void optimizeGradientStops( Gradient& rGradient )
336 // sort for increasing stop position
337 std::sort(rGradient.maStops.begin(),rGradient.maStops.end(),
338 StopSorter(maGradientStopVector));
340 if( rGradient.maStops.size() < 3 )
341 return; //easy! :-)
343 // join similar colors
344 std::vector<sal_Size> aNewStops(1,rGradient.maStops.front());
345 for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
347 if( maGradientStopVector[rGradient.maStops[i]].maStopColor !=
348 maGradientStopVector[aNewStops.back()].maStopColor )
349 aNewStops.push_back(rGradient.maStops[i]);
352 rGradient.maStops = aNewStops;
353 if (rGradient.maStops.size() < 2)
355 return; // can't optimize further...
358 // axial gradient, maybe?
359 if( rGradient.meType == Gradient::LINEAR &&
360 rGradient.maStops.size() == 3 &&
361 maGradientStopVector[rGradient.maStops.front()].maStopColor ==
362 maGradientStopVector[rGradient.maStops.back()].maStopColor )
364 // yep - keep it at that
365 return;
368 // find out most significant color difference, and limit to
369 // those two stops around this border (metric is
370 // super-simplistic: take euclidean distance of colors, weigh
371 // with stop distance)
372 sal_Size nMaxIndex=0;
373 double fMaxDistance=0.0;
374 for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
376 const double fCurrDistance(
377 colorDiffSquared(
378 maGradientStopVector[rGradient.maStops[i-1]].maStopColor,
379 maGradientStopVector[rGradient.maStops[i]].maStopColor) *
380 (square(maGradientStopVector[rGradient.maStops[i-1]].mnStopPosition) +
381 square(maGradientStopVector[rGradient.maStops[i]].mnStopPosition)) );
383 if( fCurrDistance > fMaxDistance )
385 nMaxIndex = i-1;
386 fMaxDistance = fCurrDistance;
389 rGradient.maStops[0] = rGradient.maStops[nMaxIndex];
390 rGradient.maStops[1] = rGradient.maStops[nMaxIndex+1];
391 rGradient.maStops.erase(rGradient.maStops.begin()+2,rGradient.maStops.end());
394 static sal_Int8 toByteColor( double val )
396 // TODO(Q3): duplicated from vcl::unotools
397 return sal::static_int_cast<sal_Int8>(
398 basegfx::fround(val*255.0));
401 static OUString getOdfColor( const ARGBColor& rColor )
403 // TODO(Q3): duplicated from pdfimport
404 OUStringBuffer aBuf( 7 );
405 const sal_uInt8 nRed ( toByteColor(rColor.r) );
406 const sal_uInt8 nGreen( toByteColor(rColor.g) );
407 const sal_uInt8 nBlue ( toByteColor(rColor.b) );
408 aBuf.append( '#' );
409 if( nRed < 0x10 )
410 aBuf.append( '0' );
411 aBuf.append( sal_Int32(nRed), 16 );
412 if( nGreen < 0x10 )
413 aBuf.append( '0' );
414 aBuf.append( sal_Int32(nGreen), 16 );
415 if( nBlue < 0x10 )
416 aBuf.append( '0' );
417 aBuf.append( sal_Int32(nBlue), 16 );
419 // TODO(F3): respect alpha transparency (polygons etc.)
420 OSL_ASSERT(rColor.a == 1.0);
422 return aBuf.makeStringAndClear();
425 static OUString getOdfAlign( TextAlign eAlign )
427 static const char aStart[] = "start";
428 static const char aEnd[] = "end";
429 static const char aCenter[] = "center";
430 switch(eAlign)
432 default:
433 case BEFORE:
434 return OUString(aStart);
435 case CENTER:
436 return OUString(aCenter);
437 case AFTER:
438 return OUString(aEnd);
442 bool writeStyle(State& rState, const sal_Int32 nTagId)
444 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
445 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
447 if (XML_TEXT == nTagId) {
448 rState.mbIsText = true;
449 basegfx::B2DTuple aScale, aTranslate;
450 double fRotate, fShearX;
451 if (rState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
453 rState.mnFontSize *= aScale.getX();
457 std::pair<StatePool::iterator,
458 bool> aRes = mrStates.insert(rState);
459 SAL_INFO ("svg", "size " << mrStates.size() << " id " << const_cast<State&>(*aRes.first).mnStyleId);
461 if( !aRes.second )
462 return false; // not written
464 ++mnCurrStateId;
466 // mnStyleId does not take part in hashing/comparison
467 const_cast<State&>(*aRes.first).mnStyleId = mnCurrStateId;
468 SAL_INFO ("svg", " --> " << const_cast<State&>(*aRes.first).mnStyleId);
470 mrStateMap.insert(std::make_pair(
471 mnCurrStateId,
472 rState));
474 // find two representative stop colors (as ODF only support
475 // start&end color)
476 optimizeGradientStops(rState.maFillGradient);
478 if( !mxDocumentHandler.is() )
479 return true; // cannot write style, svm import case
481 // do we have a gradient fill? then write out gradient as well
482 if( rState.meFillType == GRADIENT && rState.maFillGradient.maStops.size() > 1 )
484 // TODO(F3): ODF12 supposedly also groks svg:linear/radialGradient. But CL says: nope.
485 xAttrs->AddAttribute( "draw:name", getStyleName("svggradient", rState.maFillGradient.mnId) );
486 if( rState.maFillGradient.meType == Gradient::LINEAR )
488 // should the optimizeGradientStops method decide that
489 // this is a three-color gradient, it prolly wanted us
490 // to take axial instead
491 xAttrs->AddAttribute( "draw:style",
492 rState.maFillGradient.maStops.size() == 3 ?
493 OUString("axial") :
494 OUString("linear") );
496 else
498 xAttrs->AddAttribute( "draw:style", "ellipsoid" );
499 xAttrs->AddAttribute( "draw:cx", "50%" );
500 xAttrs->AddAttribute( "draw:cy", "50%" );
503 basegfx::B2DTuple rScale, rTranslate;
504 double rRotate, rShearX;
505 if( rState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
506 xAttrs->AddAttribute( "draw:angle",
507 OUString::number(rRotate*1800.0/M_PI ) );
508 xAttrs->AddAttribute( "draw:start-color",
509 getOdfColor(
510 maGradientStopVector[
511 rState.maFillGradient.maStops[0]].maStopColor) );
512 xAttrs->AddAttribute( "draw:end-color",
513 getOdfColor(
514 maGradientStopVector[
515 rState.maFillGradient.maStops[1]].maStopColor) );
516 xAttrs->AddAttribute( "draw:border", "0%" );
517 mxDocumentHandler->startElement( "draw:gradient", xUnoAttrs );
518 mxDocumentHandler->endElement( "draw:gradient" );
520 if( hasGradientOpacity(rState.maFillGradient) )
522 // need to write out opacity style as well
523 xAttrs->Clear();
524 xAttrs->AddAttribute( "draw:name", getStyleName("svgopacity", rState.maFillGradient.mnId) );
525 if( rState.maFillGradient.meType == Gradient::LINEAR )
527 xAttrs->AddAttribute( "draw:style", "linear" );
529 else
531 xAttrs->AddAttribute( "draw:style", "ellipsoid" );
532 xAttrs->AddAttribute( "draw:cx", "50%" );
533 xAttrs->AddAttribute( "draw:cy", "50%" );
536 // modulate gradient opacity with overall fill opacity
537 xAttrs->AddAttribute( "draw:end",
538 OUString::number(
539 maGradientStopVector[
540 rState.maFillGradient.maStops[0]].maStopColor.a*
541 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
542 xAttrs->AddAttribute( "draw:start",
543 OUString::number(
544 maGradientStopVector[
545 rState.maFillGradient.maStops[1]].maStopColor.a*
546 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
547 xAttrs->AddAttribute( "draw:border", "0%" );
548 mxDocumentHandler->startElement( "draw:opacity", xUnoAttrs );
549 mxDocumentHandler->endElement( "draw:opacity" );
553 // serialize to automatic-style section
554 if( nTagId == XML_TEXT )
556 // write paragraph style attributes
557 xAttrs->Clear();
559 xAttrs->AddAttribute( "style:name", getStyleName("svgparagraphstyle", mnCurrStateId) );
560 xAttrs->AddAttribute( "style:family", "paragraph" );
561 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
563 xAttrs->Clear();
564 xAttrs->AddAttribute( "fo:text-align", getOdfAlign(rState.meTextAnchor));
566 mxDocumentHandler->startElement( "style:paragraph-properties", xUnoAttrs );
567 mxDocumentHandler->endElement( "style:paragraph-properties" );
568 mxDocumentHandler->endElement( "style:style" );
570 // write text style attributes
571 xAttrs->Clear();
573 xAttrs->AddAttribute( "style:name", getStyleName("svgtextstyle", mnCurrStateId) );
574 xAttrs->AddAttribute( "style:family", "text" );
575 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
576 xAttrs->Clear();
577 xAttrs->AddAttribute( "fo:font-family", rState.maFontFamily);
578 xAttrs->AddAttribute( "fo:font-size",
579 OUString::number(pt2mm(rState.mnFontSize))+"mm");
580 xAttrs->AddAttribute( "fo:font-style", rState.maFontStyle);
581 xAttrs->AddAttribute( "fo:font-variant", rState.maFontVariant);
582 xAttrs->AddAttribute( "fo:font-weight",
583 OUString::number(rState.mnFontWeight));
584 xAttrs->AddAttribute( "fo:color", getOdfColor(rState.maFillColor));
586 mxDocumentHandler->startElement( "style:text-properties", xUnoAttrs );
587 mxDocumentHandler->endElement( "style:text-properties" );
588 mxDocumentHandler->endElement( "style:style" );
591 xAttrs->Clear();
592 xAttrs->AddAttribute( "style:name" , getStyleName("svggraphicstyle", mnCurrStateId) );
593 xAttrs->AddAttribute( "style:family", "graphic" );
594 mxDocumentHandler->startElement( "style:style", xUnoAttrs );
596 xAttrs->Clear();
597 // text or shape? if the former, no use in processing any
598 // graphic attributes except stroke color, ODF can do ~nothing
599 // with text shapes
600 if( nTagId == XML_TEXT )
602 //xAttrs->AddAttribute( "draw:auto-grow-height", "true");
603 xAttrs->AddAttribute( "draw:auto-grow-width", "true");
604 xAttrs->AddAttribute( "draw:textarea-horizontal-align", "left");
605 //xAttrs->AddAttribute( "draw:textarea-vertical-align", "top");
606 xAttrs->AddAttribute( "fo:min-height", "0cm");
608 xAttrs->AddAttribute( "fo:padding-top", "0cm");
609 xAttrs->AddAttribute( "fo:padding-left", "0cm");
610 xAttrs->AddAttribute( "fo:padding-right", "0cm");
611 xAttrs->AddAttribute( "fo:padding-bottom", "0cm");
613 // disable any background shape
614 xAttrs->AddAttribute( "draw:stroke", "none");
615 xAttrs->AddAttribute( "draw:fill", "none");
617 else
619 if( rState.meFillType != NONE )
621 if( rState.meFillType == GRADIENT )
623 xAttrs->AddAttribute( "draw:fill", "gradient");
624 xAttrs->AddAttribute( "draw:fill-gradient-name",
625 getStyleName("svggradient", rState.maFillGradient.mnId) );
626 if( hasGradientOpacity(rState.maFillGradient) )
628 // needs transparency gradient as well
629 xAttrs->AddAttribute( "draw:opacity-name",
630 getStyleName("svgopacity", rState.maFillGradient.mnId) );
632 else if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
633 xAttrs->AddAttribute( "draw:opacity",
634 OUString::number(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+"%" );
636 else
638 xAttrs->AddAttribute( "draw:fill", "solid");
639 xAttrs->AddAttribute( "draw:fill-color", getOdfColor(rState.maFillColor));
640 if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
641 xAttrs->AddAttribute( "draw:opacity",
642 OUString::number(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+"%" );
645 else
646 xAttrs->AddAttribute( "draw:fill", "none");
648 if( rState.meStrokeType == SOLID )
650 xAttrs->AddAttribute( "draw:stroke", "solid");
651 xAttrs->AddAttribute( "svg:stroke-color", getOdfColor(rState.maStrokeColor));
653 else if( rState.meStrokeType == DASH )
655 xAttrs->AddAttribute( "draw:stroke", "dash");
656 xAttrs->AddAttribute( "draw:stroke-dash", "dash"+OUString::number(mnCurrStateId));
657 xAttrs->AddAttribute( "svg:stroke-color", getOdfColor(rState.maStrokeColor));
659 else
660 xAttrs->AddAttribute( "draw:stroke", "none");
662 if( maCurrState.mnStrokeWidth != 0.0 )
664 ::basegfx::B2DVector aVec(maCurrState.mnStrokeWidth,0);
665 aVec *= maCurrState.maCTM;
666 xAttrs->AddAttribute( "svg:stroke-width", OUString::number( pt2mm(aVec.getLength()) )+"mm");
668 if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_MITER )
669 xAttrs->AddAttribute( "draw:stroke-linejoin", "miter");
670 else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_ROUND )
671 xAttrs->AddAttribute( "draw:stroke-linejoin", "round");
672 else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_BEVEL )
673 xAttrs->AddAttribute( "draw:stroke-linejoin", "bevel");
674 if( maCurrState.mnStrokeOpacity*maCurrState.mnOpacity != 1.0 )
675 xAttrs->AddAttribute( "svg:stroke-opacity",
676 OUString::number(100.0*maCurrState.mnStrokeOpacity*maCurrState.mnOpacity)+"%");
679 mxDocumentHandler->startElement( "style:graphic-properties", xUnoAttrs );
680 mxDocumentHandler->endElement( "style:graphic-properties" );
681 mxDocumentHandler->endElement( "style:style" );
683 return true; // newly written
686 void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, const sal_Int32 nTagId)
688 SAL_INFO ("svg", "writeStyle xElem " << xElem->getTagName());
690 sal_Int32 nStyleId=0;
691 if( writeStyle(maCurrState, nTagId) )
692 nStyleId = mnCurrStateId;
693 else
694 nStyleId = mrStates.find(maCurrState)->mnStyleId;
696 xElem->setAttribute("internal-style-ref",
697 OUString::number(
698 nStyleId)
699 +"$0");
702 void push()
704 maParentStates.push_back(maCurrState);
707 void pop()
709 maParentStates.pop_back();
712 void parseLinearGradientData( Gradient& io_rCurrGradient,
713 const sal_Int32 nGradientNumber,
714 const sal_Int32 nTokenId,
715 const OUString& sValue )
717 switch(nTokenId)
719 case XML_GRADIENTTRANSFORM:
721 OString aValueUtf8( sValue.getStr(),
722 sValue.getLength(),
723 RTL_TEXTENCODING_UTF8 );
724 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
725 break;
727 case XML_X1:
728 io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue,maCurrState,'h');
729 break;
730 case XML_X2:
731 io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue,maCurrState,'h');
732 break;
733 case XML_Y1:
734 io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue,maCurrState,'v');
735 break;
736 case XML_Y2:
737 io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue,maCurrState,'v');
738 break;
739 case XML_ID:
740 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
741 break;
742 case XML_GRADIENTUNITS:
743 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
744 io_rCurrGradient.mbBoundingBoxUnits = true;
745 else
746 io_rCurrGradient.mbBoundingBoxUnits = false;
747 break;
748 default:
749 break;
753 void parseRadialGradientData( Gradient& io_rCurrGradient,
754 const sal_Int32 nGradientNumber,
755 const sal_Int32 nTokenId,
756 const OUString& sValue )
758 switch(nTokenId)
760 case XML_GRADIENTTRANSFORM:
762 OString aValueUtf8( sValue.getStr(),
763 sValue.getLength(),
764 RTL_TEXTENCODING_UTF8 );
765 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
766 break;
768 case XML_CX:
769 io_rCurrGradient.maCoords.radial.mfCX = convLength(sValue,maCurrState,'h');
770 break;
771 case XML_CY:
772 io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue,maCurrState,'v');
773 break;
774 case XML_FX:
775 io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue,maCurrState,'h');
776 break;
777 case XML_FY:
778 io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue,maCurrState,'v');
779 break;
780 case XML_R:
781 io_rCurrGradient.maCoords.radial.mfR = convLength(sValue,maCurrState,'r');
782 break;
783 case XML_ID:
784 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
785 break;
786 case XML_GRADIENTUNITS:
787 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
788 io_rCurrGradient.mbBoundingBoxUnits = true;
789 else
790 io_rCurrGradient.mbBoundingBoxUnits = false;
791 break;
792 default:
793 break;
797 void parseGradientStop( GradientStop& io_rGradientStop,
798 const sal_Int32 nStopNumber,
799 const sal_Int32 nTokenId,
800 const OUString& sValue )
802 switch(nTokenId)
804 case XML_HREF:
806 ElementRefMapType::iterator aFound=maStopIdMap.end();
807 if ( sValue.copy(0,1) == "#" )
808 aFound = maStopIdMap.find(sValue.copy(1));
809 else
810 aFound = maStopIdMap.find(sValue);
812 if( aFound != maStopIdMap.end() )
813 io_rGradientStop = maGradientStopVector[aFound->second];
814 break;
816 case XML_ID:
817 maStopIdMap.insert(std::make_pair(sValue,nStopNumber));
818 break;
819 case XML_OFFSET:
820 io_rGradientStop.mnStopPosition = sValue.toDouble();
821 break;
822 case XML_STYLE:
823 parseStyle( sValue );
824 break;
825 default:
826 break;
830 void parseAttribute( const sal_Int32 nTokenId,
831 const OUString& sValue )
833 OString aValueUtf8( sValue.getStr(),
834 sValue.getLength(),
835 RTL_TEXTENCODING_UTF8 );
836 switch(nTokenId)
838 case XML_WIDTH:
840 const double fViewPortWidth(
841 convLength(sValue,maCurrState,'h'));
843 maCurrState.maViewport.expand(
844 basegfx::B2DTuple(fViewPortWidth,0.0));
845 break;
847 case XML_HEIGHT:
849 const double fViewPortHeight(
850 convLength(sValue,maCurrState,'v'));
852 maCurrState.maViewport.expand(
853 basegfx::B2DTuple(0.0,fViewPortHeight));
854 break;
856 case XML_VIEWBOX:
858 // TODO(F1): preserveAspectRatio
859 parseViewBox(
860 aValueUtf8.getStr(),
861 maCurrState.maViewBox);
862 break;
864 case XML_FILL_RULE:
866 if( aValueUtf8 == "evenodd" )
867 maCurrState.meFillRule = EVEN_ODD;
868 else if( aValueUtf8 == "nonzero" )
869 maCurrState.meFillRule = NON_ZERO;
870 else if( aValueUtf8 == "inherit" )
871 maCurrState.meFillRule = maParentStates.back().meFillRule;
872 break;
874 case XML_OPACITY:
875 if( aValueUtf8 == "inherit" )
876 maCurrState.mnOpacity = maParentStates.back().mnOpacity;
877 else
878 maCurrState.mnOpacity = aValueUtf8.toDouble();
879 break;
880 case XML_FILL_OPACITY:
881 if( aValueUtf8 == "inherit" )
882 maCurrState.mnFillOpacity = maParentStates.back().mnFillOpacity;
883 else {
884 maCurrState.mnFillOpacity = aValueUtf8.toDouble();
885 if( maCurrState.mnFillOpacity > 1 )
886 maCurrState.mnFillOpacity = 1;
888 break;
889 case XML_STROKE_WIDTH:
891 if( aValueUtf8 == "inherit" )
892 maCurrState.mnStrokeWidth = maParentStates.back().mnStrokeWidth;
893 else
894 maCurrState.mnStrokeWidth = convLength(sValue,maCurrState,'r');
895 break;
897 case XML_STROKE_LINECAP:
899 if( aValueUtf8 == "butt" )
900 maCurrState.meLineCap = BUTT;
901 else if( aValueUtf8 == "round" )
902 maCurrState.meLineCap = ROUND;
903 else if( aValueUtf8 == "square" )
904 maCurrState.meLineCap = RECT;
905 else if( aValueUtf8 == "inherit" )
906 maCurrState.meLineCap = maParentStates.back().meLineCap;
907 break;
909 case XML_STROKE_LINEJOIN:
911 if( aValueUtf8 == "miter" )
912 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_MITER;
913 else if( aValueUtf8 == "round" )
914 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_ROUND;
915 else if( aValueUtf8 == "bevel" )
916 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_BEVEL;
917 else if( aValueUtf8 == "inherit" )
918 maCurrState.meLineJoin = maParentStates.back().meLineJoin;
919 break;
921 case XML_STROKE_MITERLIMIT:
923 if( aValueUtf8 == "inherit" )
924 maCurrState.mnMiterLimit = maParentStates.back().mnMiterLimit;
925 else
926 maCurrState.mnMiterLimit = aValueUtf8.toDouble();
927 break;
929 case XML_STROKE_DASHOFFSET:
931 if( aValueUtf8 == "inherit" )
932 maCurrState.mnDashOffset = maParentStates.back().mnDashOffset;
933 else
934 maCurrState.mnDashOffset = convLength(sValue,maCurrState,'r');
935 break;
937 case XML_STROKE_DASHARRAY:
939 if( aValueUtf8 == "none" )
941 maCurrState.maDashArray.clear();
942 maCurrState.meStrokeType = SOLID;
944 else if( aValueUtf8 == "inherit" )
945 maCurrState.maDashArray = maParentStates.back().maDashArray;
946 else
948 parseDashArray(aValueUtf8.getStr(),
949 maCurrState.maDashArray);
950 maCurrState.meStrokeType = DASH;
952 break;
954 case XML_STROKE_OPACITY:
955 if( aValueUtf8 == "inherit" )
956 maCurrState.mnStrokeOpacity = maParentStates.back().mnStrokeOpacity;
957 else
958 maCurrState.mnStrokeOpacity = aValueUtf8.toDouble();
959 break;
960 case XML_FILL:
962 const State& rParent( maParentStates.back() );
963 parsePaint( sValue,
964 aValueUtf8.getStr(),
965 maCurrState.meFillType,
966 maCurrState.maFillColor,
967 maCurrState.maFillGradient,
968 rParent.meFillType,
969 rParent.maFillColor,
970 rParent.maFillGradient );
971 break;
973 case XML_STROKE:
975 const State& rParent( maParentStates.back() );
976 parsePaint( sValue,
977 aValueUtf8.getStr(),
978 maCurrState.meStrokeType,
979 maCurrState.maStrokeColor,
980 maCurrState.maStrokeGradient,
981 rParent.meStrokeType,
982 rParent.maStrokeColor,
983 rParent.maStrokeGradient );
984 break;
986 case XML_COLOR:
988 if( aValueUtf8 == "inherit" )
989 maCurrState.maCurrentColor = maParentStates.back().maCurrentColor;
990 else
991 parseColor(aValueUtf8.getStr(), maCurrState.maCurrentColor);
992 break;
994 case XML_TRANSFORM:
996 basegfx::B2DHomMatrix aTransform;
997 parseTransform(aValueUtf8.getStr(),aTransform);
998 maCurrState.maTransform = maCurrState.maTransform*aTransform;
999 break;
1001 case XML_FONT_FAMILY:
1002 maCurrState.maFontFamily=sValue;
1003 break;
1004 case XML_FONT_SIZE:
1005 maCurrState.mnFontSize=convLength(sValue,maCurrState,'v');
1006 break;
1007 case XML_FONT_STYLE:
1008 parseFontStyle(maCurrState,sValue,aValueUtf8.getStr());
1009 break;
1010 case XML_FONT_WEIGHT:
1011 maCurrState.mnFontWeight=sValue.toDouble();
1012 break;
1013 case XML_FONT_VARIANT:
1014 parseFontVariant(maCurrState,sValue,aValueUtf8.getStr());
1015 break;
1016 case XML_TEXT_ANCHOR:
1017 parseTextAlign(maCurrState,aValueUtf8.getStr());
1018 break;
1019 case XML_STOP_COLOR:
1020 if( maGradientVector.empty() ||
1021 maGradientVector.back().maStops.empty() )
1022 break;
1023 parseColor( aValueUtf8.getStr(),
1024 maGradientStopVector[
1025 maGradientVector.back().maStops.back()].maStopColor );
1026 break;
1027 case XML_STOP_OPACITY:
1028 if( maGradientVector.empty() ||
1029 maGradientVector.back().maStops.empty() )
1030 break;
1031 parseOpacity( aValueUtf8.getStr(),
1032 maGradientStopVector[
1033 maGradientVector.back().maStops.back()].maStopColor );
1034 break;
1035 case XML_TOKEN_INVALID:
1036 SAL_INFO("svg", "unhandled token");
1037 break;
1038 default:
1039 SAL_INFO("svg", "unhandled token " << getTokenName(nTokenId));
1040 break;
1044 void parseStyle( const OUString& sValue )
1046 // split individual style attributes
1047 sal_Int32 nIndex=0, nDummyIndex=0;
1048 OUString aCurrToken;
1051 aCurrToken=sValue.getToken(0,';',nIndex);
1053 if( !aCurrToken.isEmpty() )
1055 // split attrib & value
1056 nDummyIndex=0;
1057 OUString aCurrAttrib(
1058 aCurrToken.getToken(0,':',nDummyIndex).trim());
1059 OSL_ASSERT(nDummyIndex!=-1);
1060 nDummyIndex=0;
1061 OUString aCurrValue(
1062 aCurrToken.getToken(1,':',nDummyIndex).trim());
1063 OSL_ASSERT(nDummyIndex==-1);
1065 // recurse into normal attribute parsing
1066 parseAttribute( getTokenId(aCurrAttrib),
1067 aCurrValue );
1070 while( nIndex != -1 );
1073 static void parseFontStyle( State& io_rInitialState,
1074 const OUString& rValue,
1075 const char* sValue )
1077 if( strcmp(sValue,"inherit") != 0 )
1078 io_rInitialState.maFontStyle = rValue;
1081 static void parseFontVariant( State& io_rInitialState,
1082 const OUString& rValue,
1083 const char* sValue )
1085 if( strcmp(sValue,"inherit") != 0 )
1086 io_rInitialState.maFontVariant = rValue;
1089 static void parseTextAlign( State& io_rInitialState,
1090 const char* sValue )
1092 if( strcmp(sValue,"start") == 0 )
1093 io_rInitialState.meTextAnchor = BEFORE;
1094 else if( strcmp(sValue,"middle") == 0 )
1095 io_rInitialState.meTextAnchor = CENTER;
1096 else if( strcmp(sValue,"end") == 0 )
1097 io_rInitialState.meTextAnchor = AFTER;
1098 // keep current val for sValue == "inherit"
1101 void parsePaint( const OUString& rValue,
1102 const char* sValue,
1103 PaintType& rType,
1104 ARGBColor& rColor,
1105 Gradient& rGradient,
1106 const PaintType& rInheritType,
1107 const ARGBColor& rInheritColor,
1108 const Gradient& rInheritGradient )
1110 std::pair<const char*,const char*> aPaintUri((const char*)NULL,(const char*)NULL);
1111 std::pair<ARGBColor,bool> aColor(maCurrState.maCurrentColor,
1112 false);
1113 if( strcmp(sValue,"none") == 0 )
1114 rType = NONE;
1115 else if( strcmp(sValue,"currentColor") == 0 )
1117 rType = SOLID;
1118 rColor = maCurrState.maCurrentColor;
1120 else if( strcmp(sValue,"inherit") == 0)
1122 rType = rInheritType;
1123 rColor = rInheritColor;
1124 rGradient = rInheritGradient;
1126 else if( parsePaintUri(aPaintUri,aColor,sValue) )
1128 if( aPaintUri.first != aPaintUri.second )
1130 // assuming gradient. assumption does not hold generally
1131 if( strstr(sValue,")") && rValue.getLength() > 5 )
1133 ElementRefMapType::iterator aRes;
1134 if( (aRes=maGradientIdMap.find(
1135 rValue.copy(aPaintUri.first-sValue,
1136 aPaintUri.second-aPaintUri.first))) != maGradientIdMap.end() )
1138 rGradient = maGradientVector[aRes->second];
1139 rType = GRADIENT;
1143 else if( aColor.second )
1145 rType = SOLID;
1146 rColor = aColor.first;
1148 else
1150 rType = NONE;
1153 else
1155 rType = SOLID;
1156 parseColor(sValue,rColor);
1160 sal_Int32 mnCurrStateId;
1161 State maCurrState;
1162 std::vector<State> maParentStates;
1163 StatePool& mrStates;
1164 StateMap& mrStateMap;
1165 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1166 std::vector< Gradient > maGradientVector;
1167 std::vector< GradientStop > maGradientStopVector;
1168 ElementRefMapType maGradientIdMap;
1169 ElementRefMapType maStopIdMap;
1172 /// Annotate svg styles with unique references to state pool
1173 static void annotateStyles( StatePool& rStatePool,
1174 StateMap& rStateMap,
1175 const State& rInitialState,
1176 const uno::Reference<xml::dom::XElement>& rElem,
1177 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1179 AnnotatingVisitor aVisitor(rStatePool,rStateMap,rInitialState,xDocHdl);
1180 visitElements(aVisitor, rElem, STYLE_ANNOTATOR);
1183 struct ShapeWritingVisitor
1185 ShapeWritingVisitor(StatePool& /*rStatePool*/,
1186 StateMap& rStateMap,
1187 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1188 mrStateMap(rStateMap),
1189 mxDocumentHandler(xDocumentHandler),
1190 mnShapeNum(0)
1193 void operator()( const uno::Reference<xml::dom::XElement>& )
1197 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1198 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
1200 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1201 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1203 sal_Int32 nDummyIndex(0);
1204 OUString sStyleId(
1205 xElem->getAttribute("internal-style-ref").getToken(
1206 0,'$',nDummyIndex));
1207 StateMap::iterator pOrigState=mrStateMap.find(
1208 sStyleId.toInt32());
1210 if( pOrigState == mrStateMap.end() )
1211 return; // non-exportable element, e.g. linearGradient
1213 maCurrState = pOrigState->second;
1215 const sal_Int32 nTokenId(getTokenId(xElem->getNodeName()));
1216 switch(nTokenId)
1218 case XML_LINE:
1220 // collect attributes
1221 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1222 OUString sAttributeValue;
1223 double x1=0.0,y1=0.0,x2=0.0,y2=0.0;
1224 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1226 sAttributeValue = xAttributes->item(i)->getNodeValue();
1227 const sal_Int32 nAttribId(
1228 getTokenId(xAttributes->item(i)->getNodeName()));
1229 switch(nAttribId)
1231 case XML_X1:
1232 x1= convLength(sAttributeValue,maCurrState,'h');
1233 break;
1234 case XML_X2:
1235 x2 = convLength(sAttributeValue,maCurrState,'h');
1236 break;
1237 case XML_Y1:
1238 y1 = convLength(sAttributeValue,maCurrState,'v');
1239 break;
1240 case XML_Y2:
1241 y2 = convLength(sAttributeValue,maCurrState,'v');
1242 break;
1243 default:
1244 // skip
1245 break;
1249 if ( x1 != x2 || y1 != y2 ) {
1250 OUString sLinePath = "M"+OUString::number(x1)+","
1251 +OUString::number(y1)+"L"+OUString::number(x2)+","
1252 +OUString::number(y2);
1253 basegfx::B2DPolyPolygon aPoly;
1254 basegfx::tools::importFromSvgD(aPoly, sLinePath, false, NULL);
1256 writePathShape(xAttrs,
1257 xUnoAttrs,
1258 xElem,
1259 sStyleId,
1260 basegfx::B2DPolyPolygon(aPoly));
1263 break;
1265 case XML_POLYGON:
1266 case XML_POLYLINE:
1268 OUString sPoints = xElem->hasAttribute("points") ? xElem->getAttribute("points") : "";
1269 basegfx::B2DPolygon aPoly;
1270 (void)basegfx::tools::importFromSvgPoints(aPoly, sPoints);
1271 if( nTokenId == XML_POLYGON || maCurrState.meFillType != NONE )
1272 aPoly.setClosed(true);
1274 writePathShape(xAttrs,
1275 xUnoAttrs,
1276 xElem,
1277 sStyleId,
1278 basegfx::B2DPolyPolygon(aPoly));
1279 break;
1281 case XML_RECT:
1283 // collect attributes
1284 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1285 OUString sAttributeValue;
1286 bool bRxSeen=false, bRySeen=false;
1287 double x=0.0,y=0.0,width=0.0,height=0.0,rx=0.0,ry=0.0;
1288 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1290 sAttributeValue = xAttributes->item(i)->getNodeValue();
1291 const sal_Int32 nAttribId(
1292 getTokenId(xAttributes->item(i)->getNodeName()));
1293 switch(nAttribId)
1295 case XML_X:
1296 x = convLength(sAttributeValue,maCurrState,'h');
1297 break;
1298 case XML_Y:
1299 y = convLength(sAttributeValue,maCurrState,'v');
1300 break;
1301 case XML_WIDTH:
1302 width = convLength(sAttributeValue,maCurrState,'h');
1303 break;
1304 case XML_HEIGHT:
1305 height = convLength(sAttributeValue,maCurrState,'v');
1306 break;
1307 case XML_RX:
1308 rx = convLength(sAttributeValue,maCurrState,'h');
1309 bRxSeen=true;
1310 break;
1311 case XML_RY:
1312 ry = convLength(sAttributeValue,maCurrState,'v');
1313 bRySeen=true;
1314 break;
1315 default:
1316 // skip
1317 break;
1321 if ( (width > 0) && (height > 0) ) {
1322 if( bRxSeen && !bRySeen )
1323 ry = rx;
1324 else if( bRySeen && !bRxSeen )
1325 rx = ry;
1327 basegfx::B2DPolygon aPoly;
1328 aPoly = basegfx::tools::createPolygonFromRect(
1329 basegfx::B2DRange(x,y,x+width,y+height),
1330 rx/(0.5*width), ry/(0.5*height) );
1332 writePathShape(xAttrs,
1333 xUnoAttrs,
1334 xElem,
1335 sStyleId,
1336 basegfx::B2DPolyPolygon(aPoly));
1338 break;
1340 case XML_PATH:
1342 OUString sPath = xElem->hasAttribute("d") ? xElem->getAttribute("d") : "";
1343 basegfx::B2DPolyPolygon aPoly;
1344 basegfx::tools::importFromSvgD(aPoly, sPath, false, NULL);
1346 if ((maCurrState.meStrokeType == NONE) &&
1347 (maCurrState.meFillType != NONE) &&
1348 !aPoly.isClosed())
1350 aPoly.setClosed(true);
1353 writePathShape(xAttrs,
1354 xUnoAttrs,
1355 xElem,
1356 sStyleId,
1357 aPoly);
1358 break;
1360 case XML_CIRCLE:
1362 // collect attributes
1363 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1364 OUString sAttributeValue;
1365 double cx=0.0,cy=0.0,r=0.0;
1366 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1368 sAttributeValue = xAttributes->item(i)->getNodeValue();
1369 const sal_Int32 nAttribId(
1370 getTokenId(xAttributes->item(i)->getNodeName()));
1371 switch(nAttribId)
1373 case XML_CX:
1374 cx = convLength(sAttributeValue,maCurrState,'h');
1375 break;
1376 case XML_CY:
1377 cy = convLength(sAttributeValue,maCurrState,'v');
1378 break;
1379 case XML_R:
1380 r = convLength(sAttributeValue,maCurrState,'r');
1381 default:
1382 // skip
1383 break;
1387 if ( r > 0 )
1388 writeEllipseShape(xAttrs,
1389 xUnoAttrs,
1390 xElem,
1391 sStyleId,
1392 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r)));
1393 break;
1395 case XML_ELLIPSE:
1397 // collect attributes
1398 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1399 OUString sAttributeValue;
1400 double cx=0.0,cy=0.0,rx=0.0, ry=0.0;
1401 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1403 sAttributeValue = xAttributes->item(i)->getNodeValue();
1404 const sal_Int32 nAttribId(
1405 getTokenId(xAttributes->item(i)->getNodeName()));
1406 switch(nAttribId)
1408 case XML_CX:
1409 cx = convLength(sAttributeValue,maCurrState,'h');
1410 break;
1411 case XML_CY:
1412 cy = convLength(sAttributeValue,maCurrState,'v');
1413 break;
1414 case XML_RX:
1415 rx = convLength(sAttributeValue,maCurrState,'h');
1416 break;
1417 case XML_RY:
1418 ry = convLength(sAttributeValue,maCurrState,'v');
1419 default:
1420 // skip
1421 break;
1425 if ( rx > 0 && ry > 0 )
1426 writeEllipseShape(xAttrs,
1427 xUnoAttrs,
1428 xElem,
1429 sStyleId,
1430 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry)));
1431 break;
1433 case XML_IMAGE:
1435 // collect attributes
1436 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1437 OUString sAttributeValue;
1438 double x=0.0, y=0.0, width=0.0, height=0.0;
1439 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1441 sAttributeValue = xAttributes->item(i)->getNodeValue();
1442 const sal_Int32 nAttribId(
1443 getTokenId(xAttributes->item(i)->getNodeName()));
1444 switch(nAttribId)
1446 case XML_X:
1447 x = convLength(sAttributeValue,maCurrState,'h');
1448 break;
1449 case XML_Y:
1450 y = convLength(sAttributeValue,maCurrState,'v');
1451 break;
1452 case XML_WIDTH:
1453 width = convLength(sAttributeValue,maCurrState,'h');
1454 break;
1455 case XML_HEIGHT:
1456 height = convLength(sAttributeValue,maCurrState,'v');
1457 break;
1458 default:
1459 // skip
1460 break;
1463 // extract basic transformations out of CTM
1464 basegfx::B2DTuple aScale, aTranslate;
1465 double fRotate, fShearX;
1466 if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
1468 // apply transform
1469 x *= aScale.getX();
1470 width *= aScale.getX();
1471 y *= aScale.getY();
1472 height *= aScale.getY();
1473 x += aTranslate.getX();
1474 y += aTranslate.getY();
1475 //TODO: Rotate
1478 OUString sValue = xElem->hasAttribute("href") ? xElem->getAttribute("href") : "";
1479 OString aValueUtf8( sValue.getStr(), sValue.getLength(), RTL_TEXTENCODING_UTF8 );
1480 std::string sLinkValue;
1481 parseXlinkHref(aValueUtf8.getStr(), sLinkValue);
1483 if (!sLinkValue.empty())
1484 writeBinaryData(xAttrs, xUnoAttrs, xElem, basegfx::B2DRange(x,y,x+width,y+height), sLinkValue);
1485 break;
1487 case XML_TEXT:
1489 // collect text from all TEXT_NODE children into sText
1490 OUStringBuffer sText;
1491 visitChildren(boost::bind(
1492 (OUStringBuffer& (OUStringBuffer::*)(const OUString& str))&OUStringBuffer::append,
1493 boost::ref(sText),
1494 boost::bind(&xml::dom::XNode::getNodeValue,
1495 _1)),
1496 xElem,
1497 xml::dom::NodeType_TEXT_NODE);
1499 // collect attributes
1500 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1501 OUString sAttributeValue;
1502 double x=0.0,y=0.0;
1503 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1505 sAttributeValue = xAttributes->item(i)->getNodeValue();
1506 const sal_Int32 nAttribId(
1507 getTokenId(xAttributes->item(i)->getNodeName()));
1508 switch(nAttribId)
1510 case XML_X:
1511 x = convLength(sAttributeValue,maCurrState,'h');
1512 break;
1513 case XML_Y:
1514 y = convLength(sAttributeValue,maCurrState,'v');
1515 break;
1516 default:
1517 // skip
1518 break;
1522 // actually export text
1523 xAttrs->Clear();
1526 // extract basic transformations out of CTM
1527 basegfx::B2DTuple aScale, aTranslate;
1528 double fRotate, fShearX;
1529 if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
1531 // some heuristic attempts to have text output
1532 // baseline-relative
1533 y -= 2.0*maCurrState.mnFontSize/aScale.getX()/3.0;
1534 // apply transform
1535 x *= aScale.getX();
1536 y *= aScale.getY();
1537 x += aTranslate.getX();
1538 y += aTranslate.getY();
1539 //TODO: Rotate
1541 else {
1542 // some heuristic attempts to have text output
1543 // baseline-relative
1544 y -= 2.0*maCurrState.mnFontSize/3.0;
1547 xAttrs->AddAttribute( "svg:x", OUString::number(pt2mm(x))+"mm");
1548 xAttrs->AddAttribute( "svg:y", OUString::number(pt2mm(y))+"mm");
1549 xAttrs->AddAttribute( "draw:style-name", "svggraphicstyle"+sStyleId );
1551 mxDocumentHandler->startElement("draw:frame", xUnoAttrs);
1553 xAttrs->Clear();
1554 mxDocumentHandler->startElement("draw:text-box", xUnoAttrs);
1555 xAttrs->AddAttribute( "text:style-name", "svgparagraphstyle"+sStyleId);
1556 mxDocumentHandler->startElement("text:p", xUnoAttrs);
1558 xAttrs->Clear();
1559 xAttrs->AddAttribute( "text:style-name", "svgtextstyle"+sStyleId);
1560 mxDocumentHandler->startElement("text:span", xUnoAttrs);
1562 xAttrs->Clear();
1563 mxDocumentHandler->characters(sText.makeStringAndClear());
1564 mxDocumentHandler->endElement("text:span");
1565 mxDocumentHandler->endElement("text:p");
1566 mxDocumentHandler->endElement("draw:text-box");
1567 mxDocumentHandler->endElement("draw:frame");
1568 break;
1573 static void push()
1576 static void pop()
1579 void writeBinaryData( rtl::Reference<SvXMLAttributeList>& xAttrs,
1580 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1581 const uno::Reference<xml::dom::XElement>& /* xElem */,
1582 const basegfx::B2DRange& rShapeBounds,
1583 const std::string& data)
1585 xAttrs->Clear();
1586 xAttrs->AddAttribute( "svg:x", OUString::number(pt2mm(rShapeBounds.getMinX()))+"mm");
1587 xAttrs->AddAttribute( "svg:y", OUString::number(pt2mm(rShapeBounds.getMinY()))+"mm");
1588 xAttrs->AddAttribute( "svg:width", OUString::number(pt2mm(rShapeBounds.getWidth()))+"mm");
1589 xAttrs->AddAttribute( "svg:height", OUString::number(pt2mm(rShapeBounds.getHeight()))+"mm");
1591 mxDocumentHandler->startElement("draw:frame", xUnoAttrs);
1593 xAttrs->Clear();
1594 mxDocumentHandler->startElement("draw:image", xUnoAttrs);
1596 mxDocumentHandler->startElement("office:binary-data", xUnoAttrs);
1598 mxDocumentHandler->characters(OUString::createFromAscii(data.c_str()));
1600 mxDocumentHandler->endElement("office:binary-data");
1602 mxDocumentHandler->endElement("draw:image");
1604 mxDocumentHandler->endElement("draw:frame");
1608 void writeEllipseShape( rtl::Reference<SvXMLAttributeList>& xAttrs,
1609 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1610 const uno::Reference<xml::dom::XElement>& xElem,
1611 const OUString& rStyleId,
1612 const basegfx::B2DEllipse& rEllipse)
1614 State aState = maCurrState;
1616 xAttrs->Clear();
1618 basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(rEllipse.getB2DEllipseCenter(),
1619 rEllipse.getB2DEllipseRadius().getX(), rEllipse.getB2DEllipseRadius().getY());
1620 writePathShape(xAttrs, xUnoAttrs, xElem, rStyleId, basegfx::B2DPolyPolygon(aPoly));
1624 void writePathShape( rtl::Reference<SvXMLAttributeList>& xAttrs,
1625 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1626 const uno::Reference<xml::dom::XElement>& xElem,
1627 const OUString& rStyleId,
1628 const basegfx::B2DPolyPolygon& rPoly )
1630 // we might need to split up polypolygon into multiple path
1631 // shapes (e.g. when emulating line stroking)
1632 std::vector<basegfx::B2DPolyPolygon> aPolys(1,rPoly);
1633 State aState = maCurrState;
1634 OUString aStyleId(rStyleId);
1636 xAttrs->Clear();
1638 OSL_TRACE("writePath - the CTM is: %f %f %f %f %f %f",
1639 maCurrState.maCTM.get(0,0),
1640 maCurrState.maCTM.get(0,1),
1641 maCurrState.maCTM.get(0,2),
1642 maCurrState.maCTM.get(1,0),
1643 maCurrState.maCTM.get(1,1),
1644 maCurrState.maCTM.get(1,2));
1646 // TODO(F2): separate out shear, rotate etc.
1647 // apply transformation to polygon, to keep draw
1648 // import in 100th mm
1649 std::for_each(aPolys.begin(),aPolys.end(),
1650 boost::bind(&basegfx::B2DPolyPolygon::transform,
1651 _1,boost::cref(aState.maCTM)));
1653 for( sal_uInt32 i=0; i<aPolys.size(); ++i )
1655 const basegfx::B2DRange aBounds(
1656 aPolys[i].areControlPointsUsed() ?
1657 basegfx::tools::getRange(
1658 basegfx::tools::adaptiveSubdivideByAngle(aPolys[i])) :
1659 basegfx::tools::getRange(aPolys[i]));
1660 fillShapeProperties(xAttrs,
1661 xElem,
1662 aBounds,
1663 "svggraphicstyle"+aStyleId);
1665 // force path coordinates to 100th millimeter, after
1666 // putting polygon data at origin (ODF viewbox
1667 // calculations largely untested codepaths, as OOo always
1668 // writes "0 0 w h" viewboxes)
1669 basegfx::B2DHomMatrix aNormalize;
1670 aNormalize.translate(-aBounds.getMinX(),-aBounds.getMinY());
1671 aNormalize.scale(2540.0/72.0,2540.0/72.0);
1672 aPolys[i].transform(aNormalize);
1674 xAttrs->AddAttribute( "svg:d", basegfx::tools::exportToSvgD(
1675 aPolys[i],
1676 false, // no relative coords. causes rounding errors
1677 false, // no quad bezier detection. crashes older versions.
1678 false ));
1679 mxDocumentHandler->startElement("draw:path", xUnoAttrs);
1680 mxDocumentHandler->endElement("draw:path");
1684 void fillShapeProperties( rtl::Reference<SvXMLAttributeList>& xAttrs,
1685 const uno::Reference<xml::dom::XElement>& /* xElem */,
1686 const basegfx::B2DRange& rShapeBounds,
1687 const OUString& rStyleName )
1689 xAttrs->AddAttribute( "draw:z-index", OUString::number( mnShapeNum++ ));
1690 xAttrs->AddAttribute( "draw:style-name", rStyleName);
1691 xAttrs->AddAttribute( "svg:width", OUString::number(pt2mm(rShapeBounds.getWidth()))+"mm");
1692 xAttrs->AddAttribute( "svg:height", OUString::number(pt2mm(rShapeBounds.getHeight()))+"mm");
1694 // OOo expects the viewbox to be in 100th of mm
1695 xAttrs->AddAttribute( "svg:viewBox",
1696 "0 0 "
1697 + OUString::number(
1698 basegfx::fround(pt100thmm(rShapeBounds.getWidth())) )
1699 + " "
1700 + OUString::number(
1701 basegfx::fround(pt100thmm(rShapeBounds.getHeight())) ));
1703 // TODO(F1): decompose transformation in calling code, and use
1704 // transform attribute here
1705 // writeTranslate(maCurrState.maCTM, xAttrs);
1706 xAttrs->AddAttribute( "svg:x", OUString::number(pt2mm(rShapeBounds.getMinX()))+"mm");
1707 xAttrs->AddAttribute( "svg:y", OUString::number(pt2mm(rShapeBounds.getMinY()))+"mm");
1710 State maCurrState;
1711 StateMap& mrStateMap;
1712 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1713 sal_Int32 mnShapeNum;
1716 /// Write out shapes from DOM tree
1717 static void writeShapes( StatePool& rStatePool,
1718 StateMap& rStateMap,
1719 const uno::Reference<xml::dom::XElement>& rElem,
1720 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1722 ShapeWritingVisitor aVisitor(rStatePool,rStateMap,xDocHdl);
1723 visitElements(aVisitor, rElem, SHAPE_WRITER);
1726 } // namespace
1728 struct OfficeStylesWritingVisitor
1730 OfficeStylesWritingVisitor( StateMap& rStateMap,
1731 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1732 mrStateMap(rStateMap),
1733 mxDocumentHandler(xDocumentHandler)
1735 void operator()( const uno::Reference<xml::dom::XElement>& /*xElem*/ )
1738 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1739 const uno::Reference<xml::dom::XNamedNodeMap>& /*xAttributes*/ )
1741 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1742 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1744 sal_Int32 nDummyIndex(0);
1745 OUString sStyleId(
1746 xElem->getAttribute("internal-style-ref").getToken(
1747 0,'$',nDummyIndex));
1748 StateMap::iterator pOrigState=mrStateMap.find(
1749 sStyleId.toInt32());
1751 if( pOrigState == mrStateMap.end() )
1752 return; // non-exportable element, e.g. linearGradient
1754 maCurrState = pOrigState->second;
1756 if( maCurrState.meStrokeType == DASH )
1758 sal_Int32 dots1, dots2;
1759 double dots1_length, dots2_length, dash_distance;
1760 SvgDashArray2Odf( &dots1, &dots1_length, &dots2, &dots2_length, &dash_distance );
1762 xAttrs->Clear();
1763 xAttrs->AddAttribute( "draw:name", "dash"+sStyleId );
1764 xAttrs->AddAttribute( "draw:display-name", "dash"+sStyleId );
1765 xAttrs->AddAttribute( "draw:style", "rect" );
1766 if ( dots1>0 ) {
1767 xAttrs->AddAttribute( "draw:dots1", OUString::number(dots1) );
1768 xAttrs->AddAttribute( "draw:dots1-length", OUString::number(pt2mm(convLength( OUString::number(dots1_length), maCurrState, 'h' )))+"mm" );
1770 xAttrs->AddAttribute( "draw:distance", OUString::number(pt2mm(convLength( OUString::number(dash_distance), maCurrState, 'h' )))+"mm" );
1771 if ( dots2>0 ) {
1772 xAttrs->AddAttribute( "draw:dots2", OUString::number(dots2) );
1773 xAttrs->AddAttribute( "draw:dots2-length", OUString::number(pt2mm(convLength( OUString::number(dots2_length), maCurrState, 'h' )))+"mm" );
1776 mxDocumentHandler->startElement( "draw:stroke-dash", xUnoAttrs);
1777 mxDocumentHandler->endElement( "draw:stroke-dash" );
1781 void SvgDashArray2Odf( sal_Int32 *dots1, double *dots1_length, sal_Int32 *dots2, double *dots2_length, double *dash_distance )
1783 *dots1 = 0;
1784 *dots1_length = 0;
1785 *dots2 = 0;
1786 *dots2_length = 0;
1787 *dash_distance = 0;
1789 if( maCurrState.maDashArray.size() == 0 ) {
1790 return;
1793 double effective_dasharray_size = maCurrState.maDashArray.size();
1794 if( maCurrState.maDashArray.size() % 2 == 1 )
1795 effective_dasharray_size = maCurrState.maDashArray.size()*2;
1797 *dash_distance = maCurrState.maDashArray[1%maCurrState.maDashArray.size()];
1798 sal_Int32 dist_count = 1;
1799 for( int i=3; i<effective_dasharray_size; i+=2 ) {
1800 *dash_distance = ((dist_count * *dash_distance) + maCurrState.maDashArray[i%maCurrState.maDashArray.size()])/(dist_count+1);
1801 ++dist_count;
1804 *dots1 = 1;
1805 *dots1_length = maCurrState.maDashArray[0];
1806 int i=2;
1807 while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots1_length ) ) {
1808 ++(*dots1);
1809 i += 2;
1811 if( i<effective_dasharray_size ) {
1812 *dots2 = 1;
1813 *dots2_length = maCurrState.maDashArray[i];
1814 i+=2;
1815 while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots2_length ) ) {
1816 ++(*dots2);
1817 i += 2;
1821 SAL_INFO("svg", "SvgDashArray2Odf " << *dash_distance << " " << *dots1 << " " << *dots1_length << " " << *dots2 << " " << *dots2_length );
1823 return;
1826 static void push() {}
1827 static void pop() {}
1829 State maCurrState;
1830 StateMap& mrStateMap;
1831 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1834 static void writeOfficeStyles( StateMap& rStateMap,
1835 const uno::Reference<xml::dom::XElement>& rElem,
1836 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1838 OfficeStylesWritingVisitor aVisitor( rStateMap, xDocHdl );
1839 visitElements( aVisitor, rElem, STYLE_WRITER );
1842 #if OSL_DEBUG_LEVEL > 2
1843 struct DumpingVisitor
1845 void operator()( const uno::Reference<xml::dom::XElement>& xElem )
1847 OSL_TRACE("name: %s",
1848 OUStringToOString(
1849 xElem->getTagName(),
1850 RTL_TEXTENCODING_UTF8 ).getStr());
1853 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1854 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
1856 OSL_TRACE("name: %s",
1857 OUStringToOString(
1858 xElem->getTagName(),
1859 RTL_TEXTENCODING_UTF8 ).getStr());
1860 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1861 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1863 OSL_TRACE(" %s=%s",
1864 OUStringToOString(
1865 xAttributes->item(i)->getNodeName(),
1866 RTL_TEXTENCODING_UTF8 ).getStr(),
1867 OUStringToOString(
1868 xAttributes->item(i)->getNodeValue(),
1869 RTL_TEXTENCODING_UTF8 ).getStr());
1873 void push() {}
1874 void pop() {}
1877 static void dumpTree( const uno::Reference<xml::dom::XElement> xElem )
1879 DumpingVisitor aVisitor;
1880 visitElements(aVisitor, xElem);
1882 #endif
1885 SVGReader::SVGReader(const uno::Reference<uno::XComponentContext>& xContext,
1886 const uno::Reference<io::XInputStream>& xInputStream,
1887 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1888 m_xContext( xContext ),
1889 m_xInputStream( xInputStream ),
1890 m_xDocumentHandler( xDocumentHandler )
1894 bool SVGReader::parseAndConvert()
1896 uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder = xml::dom::DocumentBuilder::create(m_xContext);
1898 uno::Reference<xml::dom::XDocument> xDom(
1899 xDomBuilder->parse(m_xInputStream),
1900 uno::UNO_QUERY_THROW );
1902 uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(),
1903 uno::UNO_QUERY_THROW );
1905 // the root state for svg document
1906 State aInitialState;
1909 // doc boilerplate
1912 m_xDocumentHandler->startDocument();
1914 // get the document dimensions
1916 // if the "width" and "height" attributes are missing, inkscape fakes
1917 // A4 portrait for. Let's do the same.
1918 if (!xDocElem->hasAttribute("width"))
1919 xDocElem->setAttribute("width", "210mm");
1920 if (!xDocElem->hasAttribute("height"))
1921 xDocElem->setAttribute("height", "297mm");
1923 double fViewPortWidth( pt2mm(convLength(xDocElem->getAttribute("width"),aInitialState,'h')) );
1924 double fViewPortHeight( pt2mm(convLength(xDocElem->getAttribute("height"),aInitialState,'v')) );
1926 // document prolog
1927 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1928 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1930 xAttrs->AddAttribute( "xmlns:office", OASIS_STR "office:1.0" );
1931 xAttrs->AddAttribute( "xmlns:style", OASIS_STR "style:1.0" );
1932 xAttrs->AddAttribute( "xmlns:text", OASIS_STR "text:1.0" );
1933 xAttrs->AddAttribute( "xmlns:svg", OASIS_STR "svg-compatible:1.0" );
1934 xAttrs->AddAttribute( "xmlns:table", OASIS_STR "table:1.0" );
1935 xAttrs->AddAttribute( "xmlns:draw", OASIS_STR "drawing:1.0" );
1936 xAttrs->AddAttribute( "xmlns:fo", OASIS_STR "xsl-fo-compatible:1.0" );
1937 xAttrs->AddAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink");
1938 xAttrs->AddAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/");
1939 xAttrs->AddAttribute( "xmlns:number", OASIS_STR "datastyle:1.0" );
1940 xAttrs->AddAttribute( "xmlns:presentation", OASIS_STR "presentation:1.0" );
1941 xAttrs->AddAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML");
1942 xAttrs->AddAttribute( "xmlns:form", OASIS_STR "form:1.0" );
1943 xAttrs->AddAttribute( "xmlns:script", OASIS_STR "script:1.0" );
1944 xAttrs->AddAttribute( "xmlns:dom", "http://www.w3.org/2001/xml-events");
1945 xAttrs->AddAttribute( "xmlns:xforms", "http://www.w3.org/2002/xforms");
1946 xAttrs->AddAttribute( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
1947 xAttrs->AddAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1948 xAttrs->AddAttribute( "office:version", "1.0");
1949 xAttrs->AddAttribute( "office:mimetype", "application/vnd.oasis.opendocument.graphics");
1951 m_xDocumentHandler->startElement( "office:document", xUnoAttrs );
1953 xAttrs->Clear();
1955 m_xDocumentHandler->startElement( "office:settings", xUnoAttrs);
1957 xAttrs->AddAttribute( "config:name", "ooo:view-settings");
1958 m_xDocumentHandler->startElement( "config:config-item-set", xUnoAttrs);
1960 xAttrs->Clear();
1962 xAttrs->AddAttribute( "config:name", "VisibleAreaTop");
1963 xAttrs->AddAttribute( "config:type", "int");
1964 m_xDocumentHandler->startElement( "config:config-item", xUnoAttrs);
1966 m_xDocumentHandler->characters( "0" );
1968 m_xDocumentHandler->endElement( "config:config-item" );
1970 xAttrs->Clear();
1972 xAttrs->AddAttribute( "config:name", "VisibleAreaLeft" );
1973 xAttrs->AddAttribute( "config:type", "int" );
1974 m_xDocumentHandler->startElement( "config:config-item" , xUnoAttrs);
1976 m_xDocumentHandler->characters( "0" );
1978 m_xDocumentHandler->endElement( "config:config-item" );
1980 xAttrs->Clear();
1982 xAttrs->AddAttribute( "config:name" , "VisibleAreaWidth" );
1983 xAttrs->AddAttribute( "config:type" , "int" );
1984 m_xDocumentHandler->startElement( "config:config-item" , xUnoAttrs);
1986 sal_Int64 iWidth = sal_Int64(fViewPortWidth);
1987 m_xDocumentHandler->characters( OUString::number(iWidth) );
1989 m_xDocumentHandler->endElement( "config:config-item" );
1991 xAttrs->Clear();
1993 xAttrs->AddAttribute( "config:name", "VisibleAreaHeight" );
1994 xAttrs->AddAttribute( "config:type", "int" );
1995 m_xDocumentHandler->startElement( "config:config-item", xUnoAttrs);
1997 sal_Int64 iHeight = sal_Int64(fViewPortHeight);
1998 m_xDocumentHandler->characters( OUString::number(iHeight) );
2000 m_xDocumentHandler->endElement( "config:config-item" );
2002 m_xDocumentHandler->endElement( "config:config-item-set" );
2004 m_xDocumentHandler->endElement( "office:settings" );
2006 xAttrs->Clear();
2008 m_xDocumentHandler->startElement( "office:automatic-styles",
2009 xUnoAttrs );
2011 xAttrs->AddAttribute( "style:name", "pagelayout1");
2012 m_xDocumentHandler->startElement( "style:page-layout", xUnoAttrs );
2013 // TODO(Q3): this is super-ugly. In-place container come to mind.
2014 xAttrs->Clear();
2016 // make page viewport-width times viewport-height mm large - add
2017 // 5% border at every side
2018 xAttrs->AddAttribute( "fo:margin-top", "0mm");
2019 xAttrs->AddAttribute( "fo:margin-bottom", "0mm");
2020 xAttrs->AddAttribute( "fo:margin-left", "0mm");
2021 xAttrs->AddAttribute( "fo:margin-right", "0mm");
2022 xAttrs->AddAttribute( "fo:page-width", OUString::number(fViewPortWidth)+"mm");
2023 xAttrs->AddAttribute( "fo:page-height", OUString::number(fViewPortHeight)+"mm");
2024 xAttrs->AddAttribute( "style:print-orientation",
2025 fViewPortWidth > fViewPortHeight ? OUString("landscape") : OUString("portrait") );
2026 m_xDocumentHandler->startElement( "style:page-layout-properties", xUnoAttrs );
2027 m_xDocumentHandler->endElement( "style:page-layout-properties" );
2028 m_xDocumentHandler->endElement( "style:page-layout" );
2030 xAttrs->Clear();
2031 xAttrs->AddAttribute( "style:name", "pagestyle1" );
2032 xAttrs->AddAttribute( "style:family", "drawing-page" );
2033 m_xDocumentHandler->startElement( "style:style", xUnoAttrs );
2035 xAttrs->Clear();
2036 xAttrs->AddAttribute( "draw:background-size", "border");
2037 xAttrs->AddAttribute( "draw:fill", "none");
2038 m_xDocumentHandler->startElement( "style:drawing-page-properties", xUnoAttrs );
2039 m_xDocumentHandler->endElement( "style:drawing-page-properties" );
2040 m_xDocumentHandler->endElement( "style:style" );
2042 StatePool aStatePool;
2043 StateMap aStateMap;
2044 annotateStyles(aStatePool,aStateMap,aInitialState,
2045 xDocElem,m_xDocumentHandler);
2047 #if OSL_DEBUG_LEVEL > 2
2048 dumpTree(xDocElem);
2049 #endif
2051 m_xDocumentHandler->endElement( "office:automatic-styles" );
2055 xAttrs->Clear();
2056 m_xDocumentHandler->startElement( "office:styles", xUnoAttrs);
2057 writeOfficeStyles( aStateMap,
2058 xDocElem,
2059 m_xDocumentHandler);
2060 m_xDocumentHandler->endElement( "office:styles" );
2064 m_xDocumentHandler->startElement( "office:master-styles", xUnoAttrs );
2065 xAttrs->Clear();
2066 xAttrs->AddAttribute( "style:name", "Default");
2067 xAttrs->AddAttribute( "style:page-layout-name", "pagelayout1");
2068 xAttrs->AddAttribute( "draw:style-name", "pagestyle1");
2069 m_xDocumentHandler->startElement( "style:master-page", xUnoAttrs );
2070 m_xDocumentHandler->endElement( "style:master-page" );
2072 m_xDocumentHandler->endElement( "office:master-styles" );
2076 xAttrs->Clear();
2077 m_xDocumentHandler->startElement( "office:body", xUnoAttrs );
2078 m_xDocumentHandler->startElement( "office:drawing", xUnoAttrs );
2080 xAttrs->Clear();
2081 xAttrs->AddAttribute( "draw:master-page-name", "Default");
2082 xAttrs->AddAttribute( "draw:style-name", "pagestyle1");
2083 m_xDocumentHandler->startElement("draw:page", xUnoAttrs);
2085 // write out all shapes
2086 writeShapes(aStatePool,
2087 aStateMap,
2088 xDocElem,
2089 m_xDocumentHandler);
2091 m_xDocumentHandler->endElement( "draw:page" );
2092 m_xDocumentHandler->endElement( "office:drawing" );
2093 m_xDocumentHandler->endElement( "office:body" );
2094 m_xDocumentHandler->endElement( "office:document" );
2095 m_xDocumentHandler->endDocument();
2097 return true;
2100 } // namespace svgi
2102 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */