merge the formfield patch from ooo-build
[ooovba.git] / filter / source / svg / svgreader.cxx
blob8a337457512de71114eaa7b2a9f5f51f068f2c73
1 /*************************************************************************
3 * OpenOffice.org - a multi-platform office productivity suite
5 * Author:
6 * Fridrich Strba <fridrich.strba@bluewin.ch>
7 * Thorsten Behrens <tbehrens@novell.com>
9 * Copyright (C) 2008, Novell Inc.
10 * Parts copyright 2005 by Sun Microsystems, Inc.
12 * The Contents of this file are made available subject to
13 * the terms of GNU Lesser General Public License Version 2.1.
15 ************************************************************************/
17 // MARKER(update_precomp.py): autogen include statement, do not remove
18 #include "precompiled_filter.hxx"
20 #include "svgreader.hxx"
21 #include <xmloff/attrlist.hxx>
22 #include "gfxtypes.hxx"
23 #include "units.hxx"
24 #include "parserfragments.hxx"
25 #include "tokenmap.hxx"
26 #include "b2dellipse.hxx"
28 #include <rtl/math.hxx>
29 #include <rtl/ref.hxx>
30 #include <rtl/ustring.hxx>
31 #include <rtl/ustrbuf.hxx>
32 #include <basegfx/vector/b2enums.hxx>
33 #include <basegfx/range/b2drange.hxx>
34 #include <basegfx/matrix/b2dhommatrix.hxx>
35 #include <basegfx/polygon/b2dpolypolygon.hxx>
36 #include <basegfx/polygon/b2dlinegeometry.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <basegfx/polygon/b2dpolypolygontools.hxx>
39 #include <basegfx/polygon/b2dlinegeometry.hxx>
40 #include <com/sun/star/io/XSeekable.hpp>
41 #include <com/sun/star/xml/sax/XParser.hpp>
42 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
43 #include <com/sun/star/xml/dom/NodeType.hpp>
45 #include <comphelper/processfactory.hxx>
46 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
47 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
48 #include <unotools/streamwrap.hxx>
49 #include <xmloff/xmluconv.hxx>
50 #include <vcl/graph.hxx>
51 #include <vcl/virdev.hxx>
52 #include <vcl/gradient.hxx>
53 #include <svx/impgrf.hxx>
54 #include <tools/zcodec.hxx>
56 #include <boost/bind.hpp>
57 #include <hash_set>
58 #include <map>
59 #include <string.h>
61 #define USTR(x) rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( x ) )
62 #define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"
64 using namespace ::com::sun::star;
66 namespace svgi
68 namespace
71 /** visits all children of the specified type with the given functor
73 template<typename Func> void visitChildren(const Func& rFunc,
74 const uno::Reference<xml::dom::XElement> xElem,
75 xml::dom::NodeType eChildType )
77 uno::Reference<xml::dom::XNodeList> xChildren( xElem->getChildNodes() );
78 const sal_Int32 nNumNodes( xChildren->getLength() );
79 for( sal_Int32 i=0; i<nNumNodes; ++i )
81 if( xChildren->item(i)->getNodeType() == eChildType )
82 rFunc( *xChildren->item(i).get() );
86 /** Visit all elements of the given tree (in-order traversal)
88 Given functor is called for every element, and passed the
89 element's attributes, if any
91 template<typename Func> void visitElements(Func& rFunc,
92 const uno::Reference<xml::dom::XElement> xElem)
94 if( xElem->hasAttributes() )
95 rFunc(xElem,xElem->getAttributes());
96 else
97 rFunc(xElem);
99 // notify children processing
100 rFunc.push();
102 // recurse over children
103 uno::Reference<xml::dom::XNodeList> xChildren( xElem->getChildNodes() );
104 const sal_Int32 nNumNodes( xChildren->getLength() );
105 for( sal_Int32 i=0; i<nNumNodes; ++i )
107 if( xChildren->item(i)->getNodeType() == xml::dom::NodeType_ELEMENT_NODE )
108 visitElements( rFunc,
109 uno::Reference<xml::dom::XElement>(
110 xChildren->item(i),
111 uno::UNO_QUERY_THROW) );
114 // children processing done
115 rFunc.pop();
118 template<typename value_type> value_type square(value_type v)
120 return v*v;
123 double colorDiffSquared(const ARGBColor& rCol1, const ARGBColor& rCol2)
125 return
126 square(rCol1.a-rCol2.a)
127 + square(rCol1.r-rCol2.r)
128 + square(rCol1.g-rCol2.g)
129 + square(rCol1.b-rCol2.b);
132 typedef std::map<rtl::OUString,sal_Size> ElementRefMapType;
134 struct AnnotatingVisitor
136 AnnotatingVisitor(StatePool& rStatePool,
137 StateMap& rStateMap,
138 const State& rInitialState,
139 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
140 mnCurrStateId(0),
141 maCurrState(),
142 maParentStates(),
143 mrStates(rStatePool),
144 mrStateMap(rStateMap),
145 mxDocumentHandler(xDocumentHandler),
146 maGradientVector(),
147 maGradientStopVector()
149 maParentStates.push_back(rInitialState);
152 void operator()( const uno::Reference<xml::dom::XElement>& )
155 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
156 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
158 const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
159 switch (nTagId)
161 case XML_LINEARGRADIENT:
163 const sal_Int32 nNumAttrs( xAttributes->getLength() );
164 rtl::OUString sAttributeValue;
165 maGradientVector.push_back(Gradient(Gradient::LINEAR));
167 // do we have a reference to a parent gradient? parse
168 // that first, as it sets our defaults here (manually
169 // tracking default state on each Gradient variable is
170 // much more overhead)
171 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem(USTR("href")));
172 if(xNode.is())
174 const rtl::OUString sValue(xNode->getNodeValue());
175 ElementRefMapType::iterator aFound=maGradientIdMap.end();
176 if (sValue.copy(0,1).equalsAscii("#"))
177 aFound = maGradientIdMap.find(sValue.copy(1));
178 else
179 aFound = maGradientIdMap.find(sValue);;
181 if( aFound != maGradientIdMap.end() )
182 maGradientVector.back() = maGradientVector[aFound->second];
185 // do that after dereferencing, to prevent hyperlinked
186 // gradient to clobber our Id again
187 maGradientVector.back().mnId = maGradientVector.size()-1;
188 maGradientVector.back().meType = Gradient::LINEAR; // has been clobbered as well
190 for( sal_Int32 i=0; i<nNumAttrs; ++i )
192 parseLinearGradientData( maGradientVector.back(),
193 maGradientVector.size()-1,
194 getTokenId(xAttributes->item(i)->getNodeName()),
195 xAttributes->item(i)->getNodeValue() );
197 break;
199 case XML_RADIALGRADIENT:
201 const sal_Int32 nNumAttrs( xAttributes->getLength() );
202 rtl::OUString sAttributeValue;
203 maGradientVector.push_back(Gradient(Gradient::RADIAL));
205 // do we have a reference to a parent gradient? parse
206 // that first, as it sets our defaults here (manually
207 // tracking default state on each Gradient variable is
208 // much more overhead)
209 uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem(USTR("href")));
210 if(xNode.is())
212 const rtl::OUString sValue(xNode->getNodeValue());
213 ElementRefMapType::iterator aFound=maGradientIdMap.end();
214 if (sValue.copy(0,1).equalsAscii("#"))
215 aFound = maGradientIdMap.find(sValue.copy(1));
216 else
217 aFound = maGradientIdMap.find(sValue);;
219 if( aFound != maGradientIdMap.end() )
220 maGradientVector.back() = maGradientVector[aFound->second];
223 // do that after dereferencing, to prevent hyperlinked
224 // gradient to clobber our Id again
225 maGradientVector.back().mnId = maGradientVector.size()-1;
226 maGradientVector.back().meType = Gradient::RADIAL; // has been clobbered as well
228 for( sal_Int32 i=0; i<nNumAttrs; ++i )
230 parseRadialGradientData( maGradientVector.back(),
231 maGradientVector.size()-1,
232 getTokenId(xAttributes->item(i)->getNodeName()),
233 xAttributes->item(i)->getNodeValue() );
235 break;
237 case XML_STOP:
239 const sal_Int32 nNumAttrs( xAttributes->getLength() );
240 rtl::OUString sAttributeValue;
241 maGradientStopVector.push_back(GradientStop());
242 maGradientVector.back().maStops.push_back(maGradientStopVector.size()-1);
243 for( sal_Int32 i=0; i<nNumAttrs; ++i )
245 parseGradientStop( maGradientStopVector.back(),
246 maGradientStopVector.size()-1,
247 getTokenId(xAttributes->item(i)->getNodeName()),
248 xAttributes->item(i)->getNodeValue() );
250 break;
252 default:
254 // init state. inherit defaults from parent.
255 maCurrState = maParentStates.back();
256 maCurrState.maTransform.identity();
257 maCurrState.maViewBox.reset();
259 // scan for style info
260 const sal_Int32 nNumAttrs( xAttributes->getLength() );
261 rtl::OUString sAttributeValue;
262 for( sal_Int32 i=0; i<nNumAttrs; ++i )
264 sAttributeValue = xAttributes->item(i)->getNodeValue();
265 const sal_Int32 nTokenId(
266 getTokenId(xAttributes->item(i)->getNodeName()));
267 if( XML_STYLE == nTokenId )
268 parseStyle(sAttributeValue);
269 else
270 parseAttribute(nTokenId,
271 sAttributeValue);
274 // all attributes parsed, can calc total CTM now
275 basegfx::B2DHomMatrix aLocalTransform;
276 if( !maCurrState.maViewBox.isEmpty() &&
277 maCurrState.maViewBox.getWidth() != 0.0 &&
278 maCurrState.maViewBox.getHeight() != 0.0 )
280 // transform aViewBox into viewport, such that they
281 // coincide
282 aLocalTransform.translate(-maCurrState.maViewBox.getMinX(),
283 -maCurrState.maViewBox.getMinY());
284 aLocalTransform.scale(maCurrState.maViewport.getWidth()/maCurrState.maViewBox.getWidth(),
285 maCurrState.maViewport.getHeight()/maCurrState.maViewBox.getHeight());
287 maCurrState.maCTM = maCurrState.maCTM*maCurrState.maTransform*aLocalTransform;
289 OSL_TRACE("annotateStyle - CTM is: %f %f %f %f %f %f",
290 maCurrState.maCTM.get(0,0),
291 maCurrState.maCTM.get(0,1),
292 maCurrState.maCTM.get(0,2),
293 maCurrState.maCTM.get(1,0),
294 maCurrState.maCTM.get(1,1),
295 maCurrState.maCTM.get(1,2));
297 // if necessary, serialize to automatic-style section
298 writeStyle(xElem,nTagId);
303 rtl::OUString getStyleName( const char* sPrefix, sal_Int32 nId )
305 return rtl::OUString::createFromAscii(sPrefix)+rtl::OUString::valueOf(nId);
308 bool hasGradientOpacity( const Gradient& rGradient )
310 return
311 maGradientStopVector[
312 rGradient.maStops[0]].maStopColor.a != 1.0 ||
313 maGradientStopVector[
314 rGradient.maStops[1]].maStopColor.a != 1.0;
317 struct StopSorter
319 explicit StopSorter( const std::vector< GradientStop >& rStopVec ) :
320 mrStopVec(rStopVec)
323 bool operator()( sal_Size rLHS, sal_Size rRHS )
325 return mrStopVec[rLHS].mnStopPosition < mrStopVec[rRHS].mnStopPosition;
328 const std::vector< GradientStop >& mrStopVec;
331 void optimizeGradientStops( Gradient& rGradient )
333 // sort for increasing stop position
334 std::sort(rGradient.maStops.begin(),rGradient.maStops.end(),
335 StopSorter(maGradientStopVector));
337 if( rGradient.maStops.size() < 3 )
338 return; //easy! :-)
340 // join similar colors
341 std::vector<sal_Size> aNewStops(1,rGradient.maStops.front());
342 for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
344 if( maGradientStopVector[rGradient.maStops[i]].maStopColor !=
345 maGradientStopVector[aNewStops.back()].maStopColor )
346 aNewStops.push_back(rGradient.maStops[i]);
349 rGradient.maStops = aNewStops;
351 // axial gradient, maybe?
352 if( rGradient.meType == Gradient::LINEAR &&
353 rGradient.maStops.size() == 3 &&
354 maGradientStopVector[rGradient.maStops.front()].maStopColor ==
355 maGradientStopVector[rGradient.maStops.back()].maStopColor )
357 // yep - keep it at that
358 return;
361 // find out most significant color difference, and limit to
362 // those two stops around this border (metric is
363 // super-simplistic: take euclidean distance of colors, weigh
364 // with stop distance)
365 sal_Size nMaxIndex=0;
366 double fMaxDistance=0.0;
367 for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
369 const double fCurrDistance(
370 colorDiffSquared(
371 maGradientStopVector[rGradient.maStops[i-1]].maStopColor,
372 maGradientStopVector[rGradient.maStops[i]].maStopColor) *
373 (square(maGradientStopVector[rGradient.maStops[i-1]].mnStopPosition) +
374 square(maGradientStopVector[rGradient.maStops[i]].mnStopPosition)) );
376 if( fCurrDistance > fMaxDistance )
378 nMaxIndex = i-1;
379 fMaxDistance = fCurrDistance;
382 rGradient.maStops[0] = rGradient.maStops[nMaxIndex];
383 rGradient.maStops[1] = rGradient.maStops[nMaxIndex+1];
384 rGradient.maStops.erase(rGradient.maStops.begin()+2,rGradient.maStops.end());
387 sal_Int8 toByteColor( double val )
389 // TODO(Q3): duplicated from vcl::unotools
390 return sal::static_int_cast<sal_Int8>(
391 basegfx::fround(val*255.0));
394 rtl::OUString getOdfColor( const ARGBColor& rColor )
396 // TODO(Q3): duplicated from pdfimport
397 rtl::OUStringBuffer aBuf( 7 );
398 const sal_uInt8 nRed ( toByteColor(rColor.r) );
399 const sal_uInt8 nGreen( toByteColor(rColor.g) );
400 const sal_uInt8 nBlue ( toByteColor(rColor.b) );
401 aBuf.append( sal_Unicode('#') );
402 if( nRed < 10 )
403 aBuf.append( sal_Unicode('0') );
404 aBuf.append( sal_Int32(nRed), 16 );
405 if( nGreen < 10 )
406 aBuf.append( sal_Unicode('0') );
407 aBuf.append( sal_Int32(nGreen), 16 );
408 if( nBlue < 10 )
409 aBuf.append( sal_Unicode('0') );
410 aBuf.append( sal_Int32(nBlue), 16 );
412 // TODO(F3): respect alpha transparency (polygons etc.)
413 OSL_ASSERT(rColor.a == 1.0);
415 return aBuf.makeStringAndClear();
418 rtl::OUString getOdfAlign( TextAlign eAlign )
420 static ::rtl::OUString aStart(USTR("start"));
421 static ::rtl::OUString aEnd(USTR("end"));
422 // static ::rtl::OUString aJustify(USTR("justify"));
423 static ::rtl::OUString aCenter(USTR("center"));
424 switch(eAlign)
426 default:
427 case BEFORE:
428 return aStart;
429 case CENTER:
430 return aCenter;
431 case AFTER:
432 return aEnd;
436 bool writeStyle(State& rState, const sal_Int32 nTagId)
438 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
439 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
441 std::pair<StatePool::iterator,
442 bool> aRes = mrStates.insert(rState);
443 if( !aRes.second )
444 return false; // not written
446 ++mnCurrStateId;
448 // mnStyleId does not take part in hashing/comparison
449 const_cast<State&>(*aRes.first).mnStyleId = mnCurrStateId;
450 mrStateMap.insert(std::make_pair(
451 mnCurrStateId,
452 rState));
454 // find two representative stop colors (as odf only support
455 // start&end color)
456 optimizeGradientStops(rState.maFillGradient);
458 if( !mxDocumentHandler.is() )
459 return true; // cannot write style, svm import case
461 // do we have a gradient fill? then write out gradient as well
462 if( rState.meFillType == GRADIENT && rState.maFillGradient.maStops.size() > 1 )
464 // TODO(F3): ODF12 supposedly also groks svg:linear/radialGradient. But CL says: nope.
465 xAttrs->AddAttribute( USTR( "draw:name" ), getStyleName("svggradient", rState.maFillGradient.mnId) );
466 if( rState.maFillGradient.meType == Gradient::LINEAR )
468 // should the optimizeGradientStops method decide that
469 // this is a three-color gradient, it prolly wanted us
470 // to take axial instead
471 xAttrs->AddAttribute( USTR( "draw:style" ),
472 rState.maFillGradient.maStops.size() == 3 ?
473 USTR("axial") :
474 USTR("linear") );
476 else
478 xAttrs->AddAttribute( USTR( "draw:style" ), USTR("ellipsoid") );
479 xAttrs->AddAttribute( USTR( "draw:cx" ), USTR("50%") );
480 xAttrs->AddAttribute( USTR( "draw:cy" ), USTR("50%") );
483 basegfx::B2DTuple rScale, rTranslate;
484 double rRotate, rShearX;
485 if( rState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
486 xAttrs->AddAttribute( USTR( "draw:angle" ),
487 rtl::OUString::valueOf(rRotate*1800.0/M_PI ) );
488 xAttrs->AddAttribute( USTR( "draw:start-color" ),
489 getOdfColor(
490 maGradientStopVector[
491 rState.maFillGradient.maStops[0]].maStopColor) );
492 xAttrs->AddAttribute( USTR( "draw:end-color" ),
493 getOdfColor(
494 maGradientStopVector[
495 rState.maFillGradient.maStops[1]].maStopColor) );
496 xAttrs->AddAttribute( USTR( "draw:border" ), USTR("0%") );
497 mxDocumentHandler->startElement( USTR("draw:gradient"),
498 xUnoAttrs );
499 mxDocumentHandler->endElement( USTR("draw:gradient") );
501 if( hasGradientOpacity(rState.maFillGradient) )
503 // need to write out opacity style as well
504 xAttrs->Clear();
505 xAttrs->AddAttribute( USTR( "draw:name" ), getStyleName("svgopacity", rState.maFillGradient.mnId) );
506 if( rState.maFillGradient.meType == Gradient::LINEAR )
508 xAttrs->AddAttribute( USTR( "draw:style" ), USTR("linear") );
510 else
512 xAttrs->AddAttribute( USTR( "draw:style" ), USTR("ellipsoid") );
513 xAttrs->AddAttribute( USTR( "draw:cx" ), USTR("50%") );
514 xAttrs->AddAttribute( USTR( "draw:cy" ), USTR("50%") );
517 // modulate gradient opacity with overall fill opacity
518 xAttrs->AddAttribute( USTR( "draw:end" ),
519 rtl::OUString::valueOf(
520 maGradientStopVector[
521 rState.maFillGradient.maStops[0]].maStopColor.a*
522 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+USTR("%" ) );
523 xAttrs->AddAttribute( USTR( "draw:start" ),
524 rtl::OUString::valueOf(
525 maGradientStopVector[
526 rState.maFillGradient.maStops[1]].maStopColor.a*
527 maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+USTR("%" ) );
528 xAttrs->AddAttribute( USTR( "draw:border" ), USTR("0%") );
529 mxDocumentHandler->startElement( USTR("draw:opacity"),
530 xUnoAttrs );
531 mxDocumentHandler->endElement( USTR("draw:opacity") );
535 // serialize to automatic-style section
536 if( nTagId == XML_TEXT )
538 // write paragraph style attributes
539 xAttrs->Clear();
541 xAttrs->AddAttribute( USTR( "style:name" ), getStyleName("svgparagraphstyle", mnCurrStateId) );
542 xAttrs->AddAttribute( USTR( "style:family" ), USTR("paragraph") );
543 mxDocumentHandler->startElement( USTR("style:style"),
544 xUnoAttrs );
546 xAttrs->Clear();
547 xAttrs->AddAttribute( USTR( "fo:text-align"), getOdfAlign(rState.meTextAnchor));
549 mxDocumentHandler->startElement( USTR("style:paragraph-properties"),
550 xUnoAttrs );
551 mxDocumentHandler->endElement( USTR("style:paragraph-properties") );
552 mxDocumentHandler->endElement( USTR("style:style") );
554 // write text style attributes
555 xAttrs->Clear();
557 xAttrs->AddAttribute( USTR( "style:name" ), getStyleName("svgtextstyle", mnCurrStateId) );
558 xAttrs->AddAttribute( USTR( "style:family" ), USTR("text") );
559 mxDocumentHandler->startElement( USTR("style:style"),
560 xUnoAttrs );
561 xAttrs->Clear();
562 xAttrs->AddAttribute( USTR( "fo:font-family"), rState.maFontFamily);
563 xAttrs->AddAttribute( USTR( "fo:font-size"),
564 rtl::OUString::valueOf(pt2mm(rState.mnFontSize))+USTR("mm"));
565 xAttrs->AddAttribute( USTR( "fo:font-style"), rState.maFontStyle);
566 xAttrs->AddAttribute( USTR( "fo:font-variant"), rState.maFontVariant);
567 xAttrs->AddAttribute( USTR( "fo:font-weight"),
568 rtl::OUString::valueOf(rState.mnFontWeight));
569 xAttrs->AddAttribute( USTR( "fo:color"), getOdfColor(rState.maFillColor));
571 mxDocumentHandler->startElement( USTR("style:text-properties"),
572 xUnoAttrs );
573 mxDocumentHandler->endElement( USTR("style:text-properties") );
574 mxDocumentHandler->endElement( USTR("style:style") );
577 xAttrs->Clear();
578 xAttrs->AddAttribute( USTR( "style:name" ), getStyleName("svggraphicstyle", mnCurrStateId) );
579 xAttrs->AddAttribute( USTR( "style:family" ), USTR("graphic") );
580 mxDocumentHandler->startElement( USTR("style:style"),
581 xUnoAttrs );
583 xAttrs->Clear();
584 // text or shape? if the former, no use in processing any
585 // graphic attributes except stroke color, ODF can do ~nothing
586 // with text shapes
587 if( nTagId == XML_TEXT )
589 //xAttrs->AddAttribute( USTR( "draw:auto-grow-height"), USTR("true"));
590 xAttrs->AddAttribute( USTR( "draw:auto-grow-width"), USTR("true"));
591 xAttrs->AddAttribute( USTR( "draw:textarea-horizontal-align"), USTR("left"));
592 //xAttrs->AddAttribute( USTR( "draw:textarea-vertical-align"), USTR("top"));
593 xAttrs->AddAttribute( USTR( "fo:min-height"), USTR("0cm"));
595 xAttrs->AddAttribute( USTR( "fo:padding-top"), USTR("0cm"));
596 xAttrs->AddAttribute( USTR( "fo:padding-left"), USTR("0cm"));
597 xAttrs->AddAttribute( USTR( "fo:padding-right"), USTR("0cm"));
598 xAttrs->AddAttribute( USTR( "fo:padding-bottom"), USTR("0cm"));
600 // disable any background shape
601 xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("none"));
602 xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none"));
604 else
606 if( rState.meFillType != NONE )
608 if( rState.meFillType == GRADIENT )
610 xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("gradient"));
611 xAttrs->AddAttribute( USTR( "draw:fill-gradient-name" ),
612 getStyleName("svggradient", rState.maFillGradient.mnId) );
613 if( hasGradientOpacity(rState.maFillGradient) )
615 // needs transparency gradient as well
616 xAttrs->AddAttribute( USTR( "draw:opacity-name" ),
617 getStyleName("svgopacity", rState.maFillGradient.mnId) );
619 else if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
620 xAttrs->AddAttribute( USTR( "draw:opacity" ),
621 rtl::OUString::valueOf(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+USTR("%") );
623 else
625 xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("solid"));
626 xAttrs->AddAttribute( USTR( "draw:fill-color" ), getOdfColor(rState.maFillColor));
627 if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
628 xAttrs->AddAttribute( USTR( "draw:opacity" ),
629 rtl::OUString::valueOf(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+USTR("%") );
632 else
633 xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none"));
635 if( rState.meStrokeType != NONE )
637 xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("solid"));
638 xAttrs->AddAttribute( USTR( "svg:stroke-color" ), getOdfColor(rState.maStrokeColor));
640 else
641 xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("none"));
643 if( maCurrState.mnStrokeWidth != 0.0 )
645 ::basegfx::B2DVector aVec(maCurrState.mnStrokeWidth,0);
646 aVec *= maCurrState.maCTM;
647 xAttrs->AddAttribute( USTR("svg:stroke-width"), rtl::OUString::valueOf( pt2mm(aVec.getLength()) )+USTR("mm"));
649 if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_MITER )
650 xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("miter"));
651 else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_ROUND )
652 xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("round"));
653 else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_BEVEL )
654 xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("bevel"));
655 if( maCurrState.mnStrokeOpacity*maCurrState.mnOpacity != 1.0 )
656 xAttrs->AddAttribute( USTR("svg:stroke-opacity"),
657 rtl::OUString::valueOf(100.0*maCurrState.mnStrokeOpacity*maCurrState.mnOpacity)+USTR("%"));
660 mxDocumentHandler->startElement( USTR("style:graphic-properties"),
661 xUnoAttrs );
662 mxDocumentHandler->endElement( USTR("style:graphic-properties") );
663 mxDocumentHandler->endElement( USTR("style:style") );
665 return true; // newly written
668 void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, const sal_Int32 nTagId)
670 sal_Int32 nEmulatedStyleId=0;
671 if( maCurrState.maDashArray.size() &&
672 maCurrState.meStrokeType != NONE )
674 // ODF dashing is severly borked - generate filled shape
675 // instead (further down the road - here, we simply
676 // emulate a filled style with the next id)
678 // move all stroke attribs to fill, Clear stroking
679 State aEmulatedStrokeState( maCurrState );
680 aEmulatedStrokeState.meFillType = maCurrState.meStrokeType;
681 aEmulatedStrokeState.mnFillOpacity = maCurrState.mnStrokeOpacity;
682 aEmulatedStrokeState.maFillColor = maCurrState.maStrokeColor;
683 aEmulatedStrokeState.maFillGradient = maCurrState.maStrokeGradient;
684 aEmulatedStrokeState.meFillRule = EVEN_ODD;
685 aEmulatedStrokeState.meStrokeType = NONE;
687 if( writeStyle(aEmulatedStrokeState, nTagId) )
688 nEmulatedStyleId = mnCurrStateId;
689 else
690 nEmulatedStyleId = mrStates.find(aEmulatedStrokeState)->mnStyleId;
693 sal_Int32 nStyleId=0;
694 if( writeStyle(maCurrState, nTagId) )
695 nStyleId = mnCurrStateId;
696 else
697 nStyleId = mrStates.find(maCurrState)->mnStyleId;
699 xElem->setAttribute(USTR("internal-style-ref"),
700 rtl::OUString::valueOf(
701 nStyleId)
702 +USTR("$")
703 +rtl::OUString::valueOf(
704 nEmulatedStyleId));
707 void push()
709 maParentStates.push_back(maCurrState);
712 void pop()
714 maParentStates.pop_back();
717 void parseLinearGradientData( Gradient& io_rCurrGradient,
718 const sal_Int32 nGradientNumber,
719 const sal_Int32 nTokenId,
720 const rtl::OUString& sValue )
722 switch(nTokenId)
724 case XML_GRADIENTTRANSFORM:
726 rtl::OString aValueUtf8( sValue.getStr(),
727 sValue.getLength(),
728 RTL_TEXTENCODING_UTF8 );
729 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
730 break;
732 case XML_X1:
733 io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue,maCurrState,'h');
734 break;
735 case XML_X2:
736 io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue,maCurrState,'h');
737 break;
738 case XML_Y1:
739 io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue,maCurrState,'v');
740 break;
741 case XML_Y2:
742 io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue,maCurrState,'v');
743 break;
744 case XML_ID:
745 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
746 break;
747 case XML_GRADIENTUNITS:
748 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
749 io_rCurrGradient.mbBoundingBoxUnits = true;
750 else
751 io_rCurrGradient.mbBoundingBoxUnits = false;
752 break;
753 default:
754 break;
758 void parseRadialGradientData( Gradient& io_rCurrGradient,
759 const sal_Int32 nGradientNumber,
760 const sal_Int32 nTokenId,
761 const rtl::OUString& sValue )
763 switch(nTokenId)
765 case XML_GRADIENTTRANSFORM:
767 rtl::OString aValueUtf8( sValue.getStr(),
768 sValue.getLength(),
769 RTL_TEXTENCODING_UTF8 );
770 parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
771 break;
773 case XML_CX:
774 io_rCurrGradient.maCoords.radial.mfCX = convLength(sValue,maCurrState,'h');
775 break;
776 case XML_CY:
777 io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue,maCurrState,'v');
778 break;
779 case XML_FX:
780 io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue,maCurrState,'h');
781 break;
782 case XML_FY:
783 io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue,maCurrState,'v');
784 break;
785 case XML_R:
786 io_rCurrGradient.maCoords.radial.mfR = convLength(sValue,maCurrState,'r');
787 break;
788 case XML_ID:
789 maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
790 break;
791 case XML_GRADIENTUNITS:
792 if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
793 io_rCurrGradient.mbBoundingBoxUnits = true;
794 else
795 io_rCurrGradient.mbBoundingBoxUnits = false;
796 break;
797 default:
798 break;
802 void parseGradientStop( GradientStop& io_rGradientStop,
803 const sal_Int32 nStopNumber,
804 const sal_Int32 nTokenId,
805 const rtl::OUString& sValue )
807 switch(nTokenId)
809 case XML_HREF:
811 ElementRefMapType::iterator aFound=maStopIdMap.end();
812 if (sValue.copy(0,1).equalsAscii("#"))
813 aFound = maStopIdMap.find(sValue.copy(1));
814 else
815 aFound = maStopIdMap.find(sValue);;
817 if( aFound != maStopIdMap.end() )
818 io_rGradientStop = maGradientStopVector[aFound->second];
819 break;
821 case XML_ID:
822 maStopIdMap.insert(std::make_pair(sValue,nStopNumber));
823 break;
824 case XML_OFFSET:
825 io_rGradientStop.mnStopPosition = sValue.toDouble();
826 break;
827 case XML_STYLE:
828 parseStyle( sValue );
829 break;
830 default:
831 break;
835 void parseAttribute( const sal_Int32 nTokenId,
836 const rtl::OUString& sValue )
838 rtl::OString aValueUtf8( sValue.getStr(),
839 sValue.getLength(),
840 RTL_TEXTENCODING_UTF8 );
841 switch(nTokenId)
843 case XML_WIDTH:
845 const double fViewPortWidth(
846 convLength(sValue,maCurrState,'h'));
848 maCurrState.maViewport.expand(
849 basegfx::B2DTuple(fViewPortWidth,0.0));
850 break;
852 case XML_HEIGHT:
854 const double fViewPortHeight(
855 convLength(sValue,maCurrState,'v'));
857 maCurrState.maViewport.expand(
858 basegfx::B2DTuple(0.0,fViewPortHeight));
859 break;
861 case XML_VIEWBOX:
863 // TODO(F1): preserveAspectRatio
864 parseViewBox(
865 aValueUtf8,
866 maCurrState.maViewBox);
867 break;
869 case XML_FILL_RULE:
871 if( aValueUtf8 == "evenodd" )
872 maCurrState.meFillRule = EVEN_ODD;
873 else if( aValueUtf8 == "nonzero" )
874 maCurrState.meFillRule = NON_ZERO;
875 else if( aValueUtf8 == "inherit" )
876 maCurrState.meFillRule = maParentStates.back().meFillRule;
877 break;
879 case XML_OPACITY:
880 if( aValueUtf8 == "inherit" )
881 maCurrState.mnOpacity = maParentStates.back().mnOpacity;
882 else
883 maCurrState.mnOpacity = aValueUtf8.toDouble();
884 break;
885 case XML_FILL_OPACITY:
886 if( aValueUtf8 == "inherit" )
887 maCurrState.mnFillOpacity = maParentStates.back().mnFillOpacity;
888 else
889 maCurrState.mnFillOpacity = aValueUtf8.toDouble();
890 break;
891 case XML_STROKE_WIDTH:
893 if( aValueUtf8 == "inherit" )
894 maCurrState.mnStrokeWidth = maParentStates.back().mnStrokeWidth;
895 else
896 maCurrState.mnStrokeWidth = convLength(sValue,maCurrState,'r');
897 break;
899 case XML_STROKE_LINECAP:
901 if( aValueUtf8 == "butt" )
902 maCurrState.meLineCap = BUTT;
903 else if( aValueUtf8 == "round" )
904 maCurrState.meLineCap = ROUND;
905 else if( aValueUtf8 == "square" )
906 maCurrState.meLineCap = RECT;
907 else if( aValueUtf8 == "inherit" )
908 maCurrState.meLineCap = maParentStates.back().meLineCap;
909 break;
911 case XML_STROKE_LINEJOIN:
913 if( aValueUtf8 == "miter" )
914 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_MITER;
915 else if( aValueUtf8 == "round" )
916 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_ROUND;
917 else if( aValueUtf8 == "bevel" )
918 maCurrState.meLineJoin = basegfx::B2DLINEJOIN_BEVEL;
919 else if( aValueUtf8 == "inherit" )
920 maCurrState.meLineJoin = maParentStates.back().meLineJoin;
921 break;
923 case XML_STROKE_MITERLIMIT:
925 if( aValueUtf8 == "inherit" )
926 maCurrState.mnMiterLimit = maParentStates.back().mnMiterLimit;
927 else
928 maCurrState.mnMiterLimit = aValueUtf8.toDouble();
929 break;
931 case XML_STROKE_DASHOFFSET:
933 if( aValueUtf8 == "inherit" )
934 maCurrState.mnDashOffset = maParentStates.back().mnDashOffset;
935 else
936 maCurrState.mnDashOffset = convLength(sValue,maCurrState,'r');
937 break;
939 case XML_STROKE_DASHARRAY:
941 if( aValueUtf8 == "none" )
942 maCurrState.maDashArray.clear();
943 else if( aValueUtf8 == "inherit" )
944 maCurrState.maDashArray = maParentStates.back().maDashArray;
945 else
946 parseDashArray(aValueUtf8.getStr(),
947 maCurrState.maDashArray);
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_TRANSFORM:
984 basegfx::B2DHomMatrix aTransform;
985 parseTransform(aValueUtf8.getStr(),aTransform);
986 maCurrState.maTransform = maCurrState.maTransform*aTransform;
987 break;
989 case XML_FONT_FAMILY:
990 maCurrState.maFontFamily=sValue;
991 break;
992 case XML_FONT_SIZE:
993 maCurrState.mnFontSize=convLength(sValue,maCurrState,'v');
994 break;
995 case XML_FONT_STYLE:
996 parseFontStyle(maCurrState,sValue,aValueUtf8.getStr());
997 break;
998 case XML_FONT_WEIGHT:
999 maCurrState.mnFontWeight=sValue.toDouble();
1000 break;
1001 case XML_FONT_VARIANT:
1002 parseFontVariant(maCurrState,sValue,aValueUtf8.getStr());
1003 break;
1004 case XML_TEXT_ANCHOR:
1005 parseTextAlign(maCurrState,aValueUtf8.getStr());
1006 break;
1007 case XML_STOP_COLOR:
1008 if( maGradientVector.empty() ||
1009 maGradientVector.back().maStops.empty() )
1010 break;
1011 parseColor( aValueUtf8,
1012 maGradientStopVector[
1013 maGradientVector.back().maStops.back()].maStopColor );
1014 break;
1015 case XML_STOP_OPACITY:
1016 if( maGradientVector.empty() ||
1017 maGradientVector.back().maStops.empty() )
1018 break;
1019 parseOpacity( aValueUtf8,
1020 maGradientStopVector[
1021 maGradientVector.back().maStops.back()].maStopColor );
1022 break;
1023 default:
1024 OSL_TRACE("unhandled token %s", getTokenName(nTokenId));
1025 break;
1029 void parseStyle( const rtl::OUString& sValue )
1031 // split individual style attributes
1032 sal_Int32 nIndex=0, nDummyIndex=0;
1033 rtl::OUString aCurrToken;
1036 aCurrToken=sValue.getToken(0,';',nIndex);
1038 if( aCurrToken.getLength() )
1040 // split attrib & value
1041 nDummyIndex=0;
1042 rtl::OUString aCurrAttrib(
1043 aCurrToken.getToken(0,':',nDummyIndex).trim());
1044 OSL_ASSERT(nDummyIndex!=-1);
1045 nDummyIndex=0;
1046 rtl::OUString aCurrValue(
1047 aCurrToken.getToken(1,':',nDummyIndex).trim());
1048 OSL_ASSERT(nDummyIndex==-1);
1050 // recurse into normal attribute parsing
1051 parseAttribute( getTokenId(aCurrAttrib),
1052 aCurrValue );
1055 while( nIndex != -1 );
1058 void parseFontStyle( State& io_rInitialState,
1059 const rtl::OUString& rValue,
1060 const char* sValue )
1062 if( strcmp(sValue,"inherit") != 0 )
1063 io_rInitialState.maFontStyle = rValue;
1066 void parseFontVariant( State& io_rInitialState,
1067 const rtl::OUString& rValue,
1068 const char* sValue )
1070 if( strcmp(sValue,"inherit") != 0 )
1071 io_rInitialState.maFontVariant = rValue;
1074 void parseTextAlign( State& io_rInitialState,
1075 const char* sValue )
1077 if( strcmp(sValue,"start") == 0 )
1078 io_rInitialState.meTextAnchor = BEFORE;
1079 else if( strcmp(sValue,"middle") == 0 )
1080 io_rInitialState.meTextAnchor = CENTER;
1081 else if( strcmp(sValue,"end") == 0 )
1082 io_rInitialState.meTextAnchor = AFTER;
1083 // keep current val for sValue == "inherit"
1086 void parsePaint( const rtl::OUString& rValue,
1087 const char* sValue,
1088 PaintType& rType,
1089 ARGBColor& rColor,
1090 Gradient& rGradient,
1091 const PaintType& rInheritType,
1092 const ARGBColor& rInheritColor,
1093 const Gradient& rInheritGradient )
1095 std::pair<const char*,const char*> aPaintUri(NULL,NULL);
1096 std::pair<ARGBColor,bool> aColor(maCurrState.maCurrentColor,
1097 false);
1098 if( strcmp(sValue,"none") == 0 )
1099 rType = NONE;
1100 else if( strcmp(sValue,"currentColor") == 0 )
1102 rType = SOLID;
1103 rColor = maCurrState.maCurrentColor;
1105 else if( strcmp(sValue,"inherit") == 0)
1107 rType = rInheritType;
1108 rColor = rInheritColor;
1109 rGradient = rInheritGradient;
1111 else if( parsePaintUri(aPaintUri,aColor,sValue) )
1113 if( aPaintUri.first != aPaintUri.second )
1115 // assuming gradient. assumption does not hold generally
1116 const char* pClosingBracket;
1117 if( (pClosingBracket=strstr(sValue,")")) && rValue.getLength() > 5 )
1119 ElementRefMapType::iterator aRes;
1120 if( (aRes=maGradientIdMap.find(
1121 rValue.copy(aPaintUri.first-sValue,
1122 aPaintUri.second-aPaintUri.first))) != maGradientIdMap.end() )
1124 rGradient = maGradientVector[aRes->second];
1125 rType = GRADIENT;
1129 else if( aColor.second )
1131 rType = SOLID;
1132 rColor = aColor.first;
1134 else
1136 rType = NONE;
1139 else
1141 rType = SOLID;
1142 parseColor(sValue,rColor);
1146 sal_Int32 mnCurrStateId;
1147 State maCurrState;
1148 std::vector<State> maParentStates;
1149 StatePool& mrStates;
1150 StateMap& mrStateMap;
1151 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1152 std::vector< Gradient > maGradientVector;
1153 std::vector< GradientStop > maGradientStopVector;
1154 ElementRefMapType maGradientIdMap;
1155 ElementRefMapType maStopIdMap;
1158 /// Annotate svg styles with unique references to state pool
1159 static void annotateStyles( StatePool& rStatePool,
1160 StateMap& rStateMap,
1161 const State& rInitialState,
1162 const uno::Reference<xml::dom::XElement> xElem,
1163 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1165 AnnotatingVisitor aVisitor(rStatePool,rStateMap,rInitialState,xDocHdl);
1166 visitElements(aVisitor, xElem);
1169 struct ShapeWritingVisitor
1171 ShapeWritingVisitor(StatePool& /*rStatePool*/,
1172 StateMap& rStateMap,
1173 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1174 mrStateMap(rStateMap),
1175 mxDocumentHandler(xDocumentHandler),
1176 mnShapeNum(0)
1179 void operator()( const uno::Reference<xml::dom::XElement>& )
1183 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1184 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
1186 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1187 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1189 sal_Int32 nDummyIndex(0);
1190 rtl::OUString sStyleId(
1191 xElem->getAttribute(
1192 USTR("internal-style-ref")).getToken(
1193 0,'$',nDummyIndex));
1194 StateMap::iterator pOrigState=mrStateMap.find(
1195 sStyleId.toInt32());
1197 if( pOrigState == mrStateMap.end() )
1198 return; // non-exportable element, e.g. linearGradient
1200 maCurrState = pOrigState->second;
1202 const sal_Int32 nTokenId(getTokenId(xElem->getNodeName()));
1203 switch(nTokenId)
1205 case XML_LINE:
1207 // collect attributes
1208 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1209 rtl::OUString sAttributeValue;
1210 double x1=0.0,y1=0.0,x2=0.0,y2=0.0;
1211 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1213 sAttributeValue = xAttributes->item(i)->getNodeValue();
1214 const sal_Int32 nAttribId(
1215 getTokenId(xAttributes->item(i)->getNodeName()));
1216 switch(nAttribId)
1218 case XML_X1:
1219 x1= convLength(sAttributeValue,maCurrState,'h');
1220 break;
1221 case XML_X2:
1222 x2 = convLength(sAttributeValue,maCurrState,'h');
1223 break;
1224 case XML_Y1:
1225 y1 = convLength(sAttributeValue,maCurrState,'v');
1226 break;
1227 case XML_Y2:
1228 y2 = convLength(sAttributeValue,maCurrState,'v');
1229 break;
1230 default:
1231 // skip
1232 break;
1236 rtl::OUString sLinePath = USTR("M")+rtl::OUString::valueOf(x1)+USTR(",")
1237 +rtl::OUString::valueOf(y1)+USTR("L")+rtl::OUString::valueOf(x2)+USTR(",")
1238 +rtl::OUString::valueOf(y2);
1239 basegfx::B2DPolyPolygon aPoly;
1240 basegfx::tools::importFromSvgD(aPoly, sLinePath);
1242 writePathShape(xAttrs,
1243 xUnoAttrs,
1244 xElem,
1245 sStyleId,
1246 basegfx::B2DPolyPolygon(aPoly));
1247 break;
1249 case XML_POLYGON:
1250 case XML_POLYLINE:
1252 rtl::OUString sPoints = xElem->hasAttribute(USTR("points")) ? xElem->getAttribute(USTR("points")) : USTR("");
1253 basegfx::B2DPolygon aPoly;
1254 basegfx::tools::importFromSvgPoints(aPoly, sPoints);
1255 if( nTokenId == XML_POLYGON || maCurrState.meFillType != NONE )
1256 aPoly.setClosed(true);
1258 writePathShape(xAttrs,
1259 xUnoAttrs,
1260 xElem,
1261 sStyleId,
1262 basegfx::B2DPolyPolygon(aPoly));
1263 break;
1265 case XML_RECT:
1267 // collect attributes
1268 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1269 rtl::OUString sAttributeValue;
1270 bool bRxSeen=false, bRySeen=false;
1271 double x=0.0,y=0.0,width=0.0,height=0.0,rx=0.0,ry=0.0;
1272 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1274 sAttributeValue = xAttributes->item(i)->getNodeValue();
1275 const sal_Int32 nAttribId(
1276 getTokenId(xAttributes->item(i)->getNodeName()));
1277 switch(nAttribId)
1279 case XML_X:
1280 x = convLength(sAttributeValue,maCurrState,'h');
1281 break;
1282 case XML_Y:
1283 y = convLength(sAttributeValue,maCurrState,'v');
1284 break;
1285 case XML_WIDTH:
1286 width = convLength(sAttributeValue,maCurrState,'h');
1287 break;
1288 case XML_HEIGHT:
1289 height = convLength(sAttributeValue,maCurrState,'v');
1290 break;
1291 case XML_RX:
1292 rx = convLength(sAttributeValue,maCurrState,'h');
1293 bRxSeen=true;
1294 break;
1295 case XML_RY:
1296 ry = convLength(sAttributeValue,maCurrState,'v');
1297 bRySeen=true;
1298 break;
1299 default:
1300 // skip
1301 break;
1305 if( bRxSeen && !bRySeen )
1306 ry = rx;
1307 else if( bRySeen && !bRxSeen )
1308 rx = ry;
1310 basegfx::B2DPolygon aPoly;
1311 aPoly = basegfx::tools::createPolygonFromRect(
1312 basegfx::B2DRange(x,y,x+width,y+height),
1313 rx/width, ry/height );
1315 writePathShape(xAttrs,
1316 xUnoAttrs,
1317 xElem,
1318 sStyleId,
1319 basegfx::B2DPolyPolygon(aPoly));
1320 break;
1322 case XML_PATH:
1324 rtl::OUString sPath = xElem->hasAttribute(USTR("d")) ? xElem->getAttribute(USTR("d")) : USTR("");
1325 basegfx::B2DPolyPolygon aPoly;
1326 basegfx::tools::importFromSvgD(aPoly, sPath);
1328 writePathShape(xAttrs,
1329 xUnoAttrs,
1330 xElem,
1331 sStyleId,
1332 aPoly);
1333 break;
1335 case XML_CIRCLE:
1337 // collect attributes
1338 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1339 rtl::OUString sAttributeValue;
1340 double cx=0.0,cy=0.0,r=0.0;
1341 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1343 sAttributeValue = xAttributes->item(i)->getNodeValue();
1344 const sal_Int32 nAttribId(
1345 getTokenId(xAttributes->item(i)->getNodeName()));
1346 switch(nAttribId)
1348 case XML_CX:
1349 cx = convLength(sAttributeValue,maCurrState,'h');
1350 break;
1351 case XML_CY:
1352 cy = convLength(sAttributeValue,maCurrState,'v');
1353 break;
1354 case XML_R:
1355 r = convLength(sAttributeValue,maCurrState,'r');
1356 default:
1357 // skip
1358 break;
1362 writeEllipseShape(xAttrs,
1363 xUnoAttrs,
1364 xElem,
1365 sStyleId,
1366 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r)));
1367 break;
1369 case XML_ELLIPSE:
1371 // collect attributes
1372 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1373 rtl::OUString sAttributeValue;
1374 double cx=0.0,cy=0.0,rx=0.0, ry=0.0;
1375 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1377 sAttributeValue = xAttributes->item(i)->getNodeValue();
1378 const sal_Int32 nAttribId(
1379 getTokenId(xAttributes->item(i)->getNodeName()));
1380 switch(nAttribId)
1382 case XML_CX:
1383 cx = convLength(sAttributeValue,maCurrState,'h');
1384 break;
1385 case XML_CY:
1386 cy = convLength(sAttributeValue,maCurrState,'v');
1387 break;
1388 case XML_RX:
1389 rx = convLength(sAttributeValue,maCurrState,'h');
1390 break;
1391 case XML_RY:
1392 ry = convLength(sAttributeValue,maCurrState,'v');
1393 default:
1394 // skip
1395 break;
1399 writeEllipseShape(xAttrs,
1400 xUnoAttrs,
1401 xElem,
1402 sStyleId,
1403 basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry)));
1404 break;
1406 case XML_IMAGE:
1408 // collect attributes
1409 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1410 rtl::OUString sAttributeValue;
1411 double x=0.0,y=0.0,width=0.0,height=0.0;
1412 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1414 sAttributeValue = xAttributes->item(i)->getNodeValue();
1415 const sal_Int32 nAttribId(
1416 getTokenId(xAttributes->item(i)->getNodeName()));
1417 switch(nAttribId)
1419 case XML_X:
1420 x = convLength(sAttributeValue,maCurrState,'h');
1421 break;
1422 case XML_Y:
1423 y = convLength(sAttributeValue,maCurrState,'v');
1424 break;
1425 case XML_WIDTH:
1426 width = convLength(sAttributeValue,maCurrState,'h');
1427 break;
1428 case XML_HEIGHT:
1429 height = convLength(sAttributeValue,maCurrState,'v');
1430 break;
1431 default:
1432 // skip
1433 break;
1437 rtl::OUString sValue = xElem->hasAttribute(USTR("href")) ? xElem->getAttribute(USTR("href")) : USTR("");
1438 rtl::OString aValueUtf8( sValue.getStr(), sValue.getLength(), RTL_TEXTENCODING_UTF8 );
1439 std::string sLinkValue;
1440 parseXlinkHref(aValueUtf8.getStr(), sLinkValue);
1442 if (!sLinkValue.empty())
1443 writeBinaryData(xAttrs, xUnoAttrs, xElem, basegfx::B2DRange(x,y,x+width,y+height), sLinkValue);
1444 break;
1446 case XML_TEXT:
1448 // collect text from all TEXT_NODE children into sText
1449 rtl::OUStringBuffer sText;
1450 visitChildren(boost::bind(
1451 (rtl::OUStringBuffer& (rtl::OUStringBuffer::*)(const sal_Unicode* str))&rtl::OUStringBuffer::append,
1452 boost::ref(sText),
1453 boost::bind(&xml::dom::XNode::getNodeValue,
1454 _1)),
1455 xElem,
1456 xml::dom::NodeType_TEXT_NODE);
1458 // collect attributes
1459 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1460 rtl::OUString sAttributeValue;
1461 double x=0.0,y=0.0,width=0.0,height=0.0;
1462 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1464 sAttributeValue = xAttributes->item(i)->getNodeValue();
1465 const sal_Int32 nAttribId(
1466 getTokenId(xAttributes->item(i)->getNodeName()));
1467 switch(nAttribId)
1469 case XML_X:
1470 x = convLength(sAttributeValue,maCurrState,'h');
1471 break;
1472 case XML_Y:
1473 y = convLength(sAttributeValue,maCurrState,'v');
1474 break;
1475 case XML_WIDTH:
1476 width = convLength(sAttributeValue,maCurrState,'h');
1477 break;
1478 case XML_HEIGHT:
1479 height = convLength(sAttributeValue,maCurrState,'v');
1480 break;
1481 default:
1482 // skip
1483 break;
1487 // actually export text
1488 xAttrs->Clear();
1490 // some heuristic attempts to have text output
1491 // baseline-relative
1492 y -= 2.0*maCurrState.mnFontSize/3.0;
1494 // extract basic transformations out of CTM
1495 basegfx::B2DTuple aScale, aTranslate;
1496 double fRotate, fShearX;
1497 ::rtl::OUString sTransformValue;
1498 if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
1500 rtl::OUString sTransform;
1501 x += aTranslate.getX();
1502 y += aTranslate.getY();
1504 sTransform +=
1505 USTR("scale(") +
1506 rtl::OUString::valueOf(aScale.getX()) +
1507 USTR(", ") +
1508 rtl::OUString::valueOf(aScale.getX()) +
1509 USTR(")");
1511 if( fRotate )
1512 sTransform += USTR(" rotate(") + rtl::OUString::valueOf(fRotate*180.0/M_PI) + USTR(")");
1514 if( fShearX )
1515 sTransform += USTR(" skewX(") + rtl::OUString::valueOf(fShearX*180.0/M_PI) + USTR(")");
1518 xAttrs->AddAttribute( USTR( "svg:x" ), rtl::OUString::valueOf(pt2mm(x))+USTR("mm"));
1519 xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(pt2mm(y))+USTR("mm"));
1520 xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("svggraphicstyle")+sStyleId );
1522 mxDocumentHandler->startElement(USTR("draw:frame"),xUnoAttrs);
1524 xAttrs->Clear();
1525 mxDocumentHandler->startElement(USTR("draw:text-box"),xUnoAttrs);
1526 xAttrs->AddAttribute( USTR( "text:style-name" ), USTR("svgparagraphstyle")+sStyleId);
1527 mxDocumentHandler->startElement(USTR("text:p"),xUnoAttrs);
1529 xAttrs->Clear();
1530 xAttrs->AddAttribute( USTR( "text:style-name" ), USTR("svgtextstyle")+sStyleId);
1531 mxDocumentHandler->startElement(USTR("text:span"),xUnoAttrs);
1533 xAttrs->Clear();
1534 mxDocumentHandler->characters(sText.makeStringAndClear());
1535 mxDocumentHandler->endElement(USTR("text:span"));
1536 mxDocumentHandler->endElement(USTR("text:p"));
1537 mxDocumentHandler->endElement(USTR("draw:text-box"));
1538 mxDocumentHandler->endElement(USTR("draw:frame"));
1539 break;
1544 void push()
1547 void pop()
1550 void writeBinaryData( rtl::Reference<SvXMLAttributeList>& xAttrs,
1551 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1552 const uno::Reference<xml::dom::XElement>& /* xElem */,
1553 const basegfx::B2DRange& rShapeBounds,
1554 const std::string& data)
1556 xAttrs->Clear();
1557 xAttrs->AddAttribute( USTR( "svg:x" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinX()))+USTR("mm"));
1558 xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinY()))+USTR("mm"));
1559 xAttrs->AddAttribute( USTR( "svg:width" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getWidth()))+USTR("mm"));
1560 xAttrs->AddAttribute( USTR( "svg:height" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getHeight()))+USTR("mm"));
1562 mxDocumentHandler->startElement(USTR("draw:frame"),xUnoAttrs);
1564 xAttrs->Clear();
1565 mxDocumentHandler->startElement(USTR("draw:image"),xUnoAttrs);
1567 mxDocumentHandler->startElement(USTR("office:binary-data"),xUnoAttrs);
1569 mxDocumentHandler->characters(rtl::OUString::createFromAscii(data.c_str()));
1571 mxDocumentHandler->endElement(USTR("office:binary-data"));
1573 mxDocumentHandler->endElement(USTR("draw:image"));
1575 mxDocumentHandler->endElement(USTR("draw:frame"));
1579 void writeTransformAttribute(const basegfx::B2DHomMatrix rMatrix, rtl::Reference<SvXMLAttributeList>& xAttrs)
1581 basegfx::B2DTuple rScale, rTranslate;
1582 double rRotate, rShearX;
1583 ::rtl::OUString sTransformValue;
1584 if (!rMatrix.decompose(rScale, rTranslate, rRotate, rShearX))
1585 return;
1586 if (rScale.getX() != 1.0 || rScale.getY() != 1.0)
1587 sTransformValue += USTR("scale(")+::rtl::OUString::valueOf(rScale.getX())+USTR(" ")
1588 +::rtl::OUString::valueOf(rScale.getY())+USTR(") ");
1589 if (rTranslate.getX() != 0.0f || rTranslate.getY() != 0.0f)
1590 sTransformValue += USTR("translate(")+::rtl::OUString::valueOf(rTranslate.getX()/100.0f)+USTR("mm ")
1591 +::rtl::OUString::valueOf(rTranslate.getY()/100.0f)+USTR("mm) ");
1592 if (rRotate != 0.0f)
1593 sTransformValue += USTR("rotate(")+::rtl::OUString::valueOf(rRotate)+USTR(") ");
1595 if (rShearX != 0.0f)
1596 sTransformValue += USTR("skewX(")+::rtl::OUString::valueOf(rShearX)+USTR(") ");
1597 if (!sTransformValue.getLength())
1598 return;
1599 xAttrs->AddAttribute( USTR("draw:transform"), sTransformValue);
1602 void writeEllipseShape( rtl::Reference<SvXMLAttributeList>& xAttrs,
1603 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1604 const uno::Reference<xml::dom::XElement>& xElem,
1605 const rtl::OUString& rStyleId,
1606 const basegfx::B2DEllipse& rEllipse)
1608 State aState = maCurrState;
1609 rtl::OUString aStyleId(rStyleId);
1611 xAttrs->Clear();
1613 basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(rEllipse.getB2DEllipseCenter(),
1614 rEllipse.getB2DEllipseRadius().getX(), rEllipse.getB2DEllipseRadius().getY());
1615 writePathShape(xAttrs, xUnoAttrs, xElem, rStyleId, basegfx::B2DPolyPolygon(aPoly));
1619 void writePathShape( rtl::Reference<SvXMLAttributeList>& xAttrs,
1620 const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
1621 const uno::Reference<xml::dom::XElement>& xElem,
1622 const rtl::OUString& rStyleId,
1623 const basegfx::B2DPolyPolygon& rPoly )
1625 // we might need to split up polypolygon into multiple path
1626 // shapes (e.g. when emulating line stroking)
1627 std::vector<basegfx::B2DPolyPolygon> aPolys(1,rPoly);
1628 State aState = maCurrState;
1629 rtl::OUString aStyleId(rStyleId);
1631 xAttrs->Clear();
1633 OSL_TRACE("writePath - the CTM is: %f %f %f %f %f %f",
1634 maCurrState.maCTM.get(0,0),
1635 maCurrState.maCTM.get(0,1),
1636 maCurrState.maCTM.get(0,2),
1637 maCurrState.maCTM.get(1,0),
1638 maCurrState.maCTM.get(1,1),
1639 maCurrState.maCTM.get(1,2));
1641 if( aState.maDashArray.size() )
1643 // ODF dashing is severly borked - generate filled polygon instead
1644 aPolys.clear();
1645 for( sal_uInt32 i=0; i<rPoly.count(); ++i )
1647 aPolys.push_back(
1648 basegfx::tools::stripNeutralPolygons(
1649 basegfx::tools::prepareForPolygonOperation(
1650 basegfx::tools::createAreaGeometry(
1651 rPoly.getB2DPolygon(i),
1652 aState.mnStrokeWidth/2.0,
1653 aState.meLineJoin))));
1654 // TODO(F2): line ends
1657 sal_Int32 nDummyIndex(0);
1658 aStyleId = xElem->getAttribute(
1659 USTR("internal-style-ref")).getToken(1,'$',nDummyIndex);
1660 StateMap::iterator pAlternateState=mrStateMap.find(aStyleId.toInt32());
1661 OSL_ASSERT(pAlternateState != mrStateMap.end());
1662 aState = pAlternateState->second;
1663 OSL_ENSURE( pAlternateState == mrStateMap.end(),
1664 "Doh - where's my alternate style entry?!" );
1667 // TODO(F2): separate out shear, rotate etc.
1668 // apply transformation to polygon, to keep draw
1669 // import in 100th mm
1670 std::for_each(aPolys.begin(),aPolys.end(),
1671 boost::bind(&basegfx::B2DPolyPolygon::transform,
1672 _1,boost::cref(aState.maCTM)));
1674 for( sal_uInt32 i=0; i<aPolys.size(); ++i )
1676 const basegfx::B2DRange aBounds(
1677 aPolys[i].areControlPointsUsed() ?
1678 basegfx::tools::getRange(
1679 basegfx::tools::adaptiveSubdivideByAngle(aPolys[i])) :
1680 basegfx::tools::getRange(aPolys[i]));
1681 fillShapeProperties(xAttrs,
1682 xElem,
1683 aBounds,
1684 USTR("svggraphicstyle")+aStyleId);
1686 // force path coordinates to 100th millimeter, after
1687 // putting polygon data at origin (odf viewbox
1688 // calculations largely untested codepaths, as OOo always
1689 // writes "0 0 w h" viewboxes)
1690 basegfx::B2DHomMatrix aNormalize;
1691 aNormalize.translate(-aBounds.getMinX(),-aBounds.getMinY());
1692 aNormalize.scale(2540.0/72.0,2540.0/72.0);
1693 aPolys[i].transform(aNormalize);
1695 xAttrs->AddAttribute( USTR( "svg:d" ), basegfx::tools::exportToSvgD(
1696 aPolys[i],
1697 false, // no relative coords. causes rounding errors
1698 false )); // no quad bezier detection. crashes older versions.
1699 mxDocumentHandler->startElement(USTR("draw:path"),
1700 xUnoAttrs);
1701 mxDocumentHandler->endElement(USTR("draw:path"));
1705 void fillShapeProperties( rtl::Reference<SvXMLAttributeList>& xAttrs,
1706 const uno::Reference<xml::dom::XElement>& /* xElem */,
1707 const basegfx::B2DRange& rShapeBounds,
1708 const rtl::OUString& rStyleName )
1710 xAttrs->AddAttribute( USTR( "draw:z-index" ), rtl::OUString::valueOf( mnShapeNum++ ));
1711 xAttrs->AddAttribute( USTR( "draw:style-name" ), rStyleName);
1712 xAttrs->AddAttribute( USTR( "svg:width" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getWidth()))+USTR("mm"));
1713 xAttrs->AddAttribute( USTR( "svg:height" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getHeight()))+USTR("mm"));
1715 // OOo expects the viewbox to be in 100th of mm
1716 xAttrs->AddAttribute( USTR( "svg:viewBox" ),
1717 USTR("0 0 ")
1718 + rtl::OUString::valueOf(
1719 basegfx::fround(pt100thmm(rShapeBounds.getWidth())) )
1720 + USTR(" ")
1721 + rtl::OUString::valueOf(
1722 basegfx::fround(pt100thmm(rShapeBounds.getHeight())) ));
1724 // TODO(F1): decompose transformation in calling code, and use
1725 // transform attribute here
1726 // writeTranslate(maCurrState.maCTM, xAttrs);
1727 xAttrs->AddAttribute( USTR( "svg:x" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinX()))+USTR("mm"));
1728 xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinY()))+USTR("mm"));
1731 State maCurrState;
1732 StateMap& mrStateMap;
1733 uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
1734 sal_Int32 mnShapeNum;
1737 /// Write out shapes from DOM tree
1738 static void writeShapes( StatePool& rStatePool,
1739 StateMap& rStateMap,
1740 const uno::Reference<xml::dom::XElement> xElem,
1741 const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
1743 ShapeWritingVisitor aVisitor(rStatePool,rStateMap,xDocHdl);
1744 visitElements(aVisitor, xElem);
1747 #ifdef VERBOSE
1748 struct DumpingVisitor
1750 void operator()( const uno::Reference<xml::dom::XElement>& xElem )
1752 OSL_TRACE("name: %s",
1753 rtl::OUStringToOString(
1754 xElem->getTagName(),
1755 RTL_TEXTENCODING_UTF8 ).getStr());
1758 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
1759 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
1761 OSL_TRACE("name: %s",
1762 rtl::OUStringToOString(
1763 xElem->getTagName(),
1764 RTL_TEXTENCODING_UTF8 ).getStr());
1765 const sal_Int32 nNumAttrs( xAttributes->getLength() );
1766 for( sal_Int32 i=0; i<nNumAttrs; ++i )
1768 OSL_TRACE(" %s=%s",
1769 rtl::OUStringToOString(
1770 xAttributes->item(i)->getNodeName(),
1771 RTL_TEXTENCODING_UTF8 ).getStr(),
1772 rtl::OUStringToOString(
1773 xAttributes->item(i)->getNodeValue(),
1774 RTL_TEXTENCODING_UTF8 ).getStr());
1778 void push() {}
1779 void pop() {}
1782 static void dumpTree( const uno::Reference<xml::dom::XElement> xElem )
1784 DumpingVisitor aVisitor;
1785 visitElements(aVisitor, xElem);
1787 #endif
1789 } // namespace
1792 SVGReader::SVGReader(const uno::Reference<lang::XMultiServiceFactory>& xServiceFactory,
1793 const uno::Reference<io::XInputStream>& xInputStream,
1794 const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
1795 m_xServiceFactory( xServiceFactory ),
1796 m_xInputStream( xInputStream ),
1797 m_xDocumentHandler( xDocumentHandler )
1801 sal_Bool SVGReader::parseAndConvert()
1803 uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(
1804 m_xServiceFactory->createInstance(
1805 rtl::OUString::createFromAscii("com.sun.star.xml.dom.DocumentBuilder")), uno::UNO_QUERY );
1807 uno::Reference<xml::dom::XDocument> xDom(
1808 xDomBuilder->parse(m_xInputStream),
1809 uno::UNO_QUERY_THROW );
1811 uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(),
1812 uno::UNO_QUERY_THROW );
1814 // the root state for svg document
1815 State aInitialState;
1817 /////////////////////////////////////////////////////////////////
1818 // doc boilerplate
1819 /////////////////////////////////////////////////////////////////
1821 m_xDocumentHandler->startDocument();
1823 // get the document dimensions
1825 // if the "width" and "height" attributes are missing, inkscape fakes
1826 // A4 portrait for. Let's do the same.
1827 if (!xDocElem->hasAttribute(USTR("width")))
1828 xDocElem->setAttribute(USTR("width"), USTR("210mm"));
1829 if (!xDocElem->hasAttribute(USTR("height")))
1830 xDocElem->setAttribute(USTR("height"), USTR("297mm"));
1832 double fViewPortWidth( pt2mm(convLength(xDocElem->getAttribute(USTR("width")),aInitialState,'h')) );
1833 double fViewPortHeight( pt2mm(convLength(xDocElem->getAttribute(USTR("height")),aInitialState,'v')) );
1835 // document prolog
1836 rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
1837 uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
1839 xAttrs->AddAttribute( USTR( "xmlns:office" ), USTR( OASIS_STR "office:1.0" ));
1840 xAttrs->AddAttribute( USTR( "xmlns:style" ), USTR( OASIS_STR "style:1.0" ));
1841 xAttrs->AddAttribute( USTR( "xmlns:text" ), USTR( OASIS_STR "text:1.0" ));
1842 xAttrs->AddAttribute( USTR( "xmlns:svg" ), USTR( OASIS_STR "svg-compatible:1.0" ));
1843 xAttrs->AddAttribute( USTR( "xmlns:table" ), USTR( OASIS_STR "table:1.0" ));
1844 xAttrs->AddAttribute( USTR( "xmlns:draw" ), USTR( OASIS_STR "drawing:1.0" ));
1845 xAttrs->AddAttribute( USTR( "xmlns:fo" ), USTR( OASIS_STR "xsl-fo-compatible:1.0" ));
1846 xAttrs->AddAttribute( USTR( "xmlns:xlink" ), USTR( "http://www.w3.org/1999/xlink" ));
1847 xAttrs->AddAttribute( USTR( "xmlns:dc" ), USTR( "http://purl.org/dc/elements/1.1/" ));
1848 xAttrs->AddAttribute( USTR( "xmlns:number" ), USTR( OASIS_STR "datastyle:1.0" ));
1849 xAttrs->AddAttribute( USTR( "xmlns:presentation" ), USTR( OASIS_STR "presentation:1.0" ));
1850 xAttrs->AddAttribute( USTR( "xmlns:math" ), USTR( "http://www.w3.org/1998/Math/MathML" ));
1851 xAttrs->AddAttribute( USTR( "xmlns:form" ), USTR( OASIS_STR "form:1.0" ));
1852 xAttrs->AddAttribute( USTR( "xmlns:script" ), USTR( OASIS_STR "script:1.0" ));
1853 xAttrs->AddAttribute( USTR( "xmlns:dom" ), USTR( "http://www.w3.org/2001/xml-events" ));
1854 xAttrs->AddAttribute( USTR( "xmlns:xforms" ), USTR( "http://www.w3.org/2002/xforms" ));
1855 xAttrs->AddAttribute( USTR( "xmlns:xsd" ), USTR( "http://www.w3.org/2001/XMLSchema" ));
1856 xAttrs->AddAttribute( USTR( "xmlns:xsi" ), USTR( "http://www.w3.org/2001/XMLSchema-instance" ));
1857 xAttrs->AddAttribute( USTR( "office:version" ), USTR( "1.0" ));
1858 xAttrs->AddAttribute( USTR( "office:mimetype" ), USTR( "application/vnd.oasis.opendocument.graphics" ));
1860 m_xDocumentHandler->startElement( USTR("office:document"), xUnoAttrs );
1862 xAttrs->Clear();
1864 m_xDocumentHandler->startElement( USTR("office:settings"), xUnoAttrs);
1866 xAttrs->AddAttribute( USTR( "config:name" ), USTR( "ooo:view-settings" ));
1867 m_xDocumentHandler->startElement( USTR("config:config-item-set"), xUnoAttrs);
1869 xAttrs->Clear();
1871 xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaTop" ));
1872 xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" ));
1873 m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs);
1875 m_xDocumentHandler->characters( USTR( "0" ));
1877 m_xDocumentHandler->endElement( USTR( "config:config-item" ));
1879 xAttrs->Clear();
1881 xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaLeft" ));
1882 xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" ));
1883 m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs);
1885 m_xDocumentHandler->characters( USTR( "0" ));
1887 m_xDocumentHandler->endElement( USTR( "config:config-item" ));
1889 xAttrs->Clear();
1891 xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaWidth" ));
1892 xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" ));
1893 m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs);
1895 sal_Int64 iWidth = sal_Int64(fViewPortWidth);
1896 m_xDocumentHandler->characters( ::rtl::OUString::valueOf(iWidth) );
1898 m_xDocumentHandler->endElement( USTR( "config:config-item" ));
1900 xAttrs->Clear();
1902 xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaHeight" ));
1903 xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" ));
1904 m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs);
1906 sal_Int64 iHeight = sal_Int64(fViewPortHeight);
1907 m_xDocumentHandler->characters( ::rtl::OUString::valueOf(iHeight) );
1909 m_xDocumentHandler->endElement( USTR( "config:config-item" ));
1911 m_xDocumentHandler->endElement( USTR( "config:config-item-set" ));
1913 m_xDocumentHandler->endElement( USTR( "office:settings" ));
1915 xAttrs->Clear();
1917 m_xDocumentHandler->startElement( USTR("office:automatic-styles"),
1918 xUnoAttrs );
1920 xAttrs->AddAttribute( USTR( "style:name" ), USTR("pagelayout1"));
1921 m_xDocumentHandler->startElement( USTR("style:page-layout"),
1922 xUnoAttrs );
1923 // TODO(Q3): this is super-ugly. In-place container come to mind.
1924 xAttrs->Clear();
1926 // make page viewport-width times viewport-height mm large - add
1927 // 5% border at every side
1928 xAttrs->AddAttribute( USTR( "fo:margin-top" ), USTR("0mm"));
1929 xAttrs->AddAttribute( USTR( "fo:margin-bottom" ), USTR("0mm"));
1930 xAttrs->AddAttribute( USTR( "fo:margin-left" ), USTR("0mm"));
1931 xAttrs->AddAttribute( USTR( "fo:margin-right" ), USTR("0mm"));
1932 xAttrs->AddAttribute( USTR( "fo:page-width" ), rtl::OUString::valueOf(fViewPortWidth)+USTR("mm"));
1933 xAttrs->AddAttribute( USTR( "fo:page-height" ), rtl::OUString::valueOf(fViewPortHeight)+USTR("mm"));
1934 xAttrs->AddAttribute( USTR( "style:print-orientation" ),
1935 fViewPortWidth > fViewPortHeight ?
1936 USTR("landscape") :
1937 USTR("portrait"));
1938 m_xDocumentHandler->startElement( USTR("style:page-layout-properties"),
1939 xUnoAttrs );
1940 m_xDocumentHandler->endElement( USTR("style:page-layout-properties") );
1941 m_xDocumentHandler->endElement( USTR("style:page-layout") );
1943 xAttrs->Clear();
1944 xAttrs->AddAttribute( USTR( "style:name" ), USTR("pagestyle1"));
1945 xAttrs->AddAttribute( USTR( "style:family" ), USTR("drawing-page"));
1946 m_xDocumentHandler->startElement( USTR("style:style"),
1947 xUnoAttrs );
1949 xAttrs->Clear();
1950 xAttrs->AddAttribute( USTR( "draw:background-size" ), USTR("border"));
1951 xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none"));
1952 m_xDocumentHandler->startElement( USTR("style:drawing-page-properties"),
1953 xUnoAttrs );
1954 m_xDocumentHandler->endElement( USTR("style:drawing-page-properties") );
1955 m_xDocumentHandler->endElement( USTR("style:style") );
1957 StatePool aStatePool;
1958 StateMap aStateMap;
1959 annotateStyles(aStatePool,aStateMap,aInitialState,
1960 xDocElem,m_xDocumentHandler);
1962 #ifdef VERBOSE
1963 dumpTree(xDocElem);
1964 #endif
1966 m_xDocumentHandler->endElement( USTR("office:automatic-styles") );
1968 ////////////////////////////////////////////////////////////////////
1970 xAttrs->Clear();
1971 m_xDocumentHandler->startElement( USTR("office:styles"),
1972 xUnoAttrs);
1973 m_xDocumentHandler->endElement( USTR("office:styles") );
1975 ////////////////////////////////////////////////////////////////////
1977 m_xDocumentHandler->startElement( USTR("office:master-styles"),
1978 xUnoAttrs );
1979 xAttrs->Clear();
1980 xAttrs->AddAttribute( USTR( "style:name" ), USTR("Default"));
1981 xAttrs->AddAttribute( USTR( "style:page-layout-name" ), USTR("pagelayout1"));
1982 xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("pagestyle1"));
1983 m_xDocumentHandler->startElement( USTR("style:master-page"),
1984 xUnoAttrs );
1985 m_xDocumentHandler->endElement( USTR("style:master-page") );
1987 m_xDocumentHandler->endElement( USTR("office:master-styles") );
1989 ////////////////////////////////////////////////////////////////////
1991 xAttrs->Clear();
1992 m_xDocumentHandler->startElement( USTR("office:body"),
1993 xUnoAttrs );
1994 m_xDocumentHandler->startElement( USTR("office:drawing"),
1995 xUnoAttrs );
1997 xAttrs->Clear();
1998 xAttrs->AddAttribute( USTR( "draw:master-page-name" ), USTR("Default"));
1999 xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("pagestyle1"));
2000 m_xDocumentHandler->startElement(USTR("draw:page"),
2001 xUnoAttrs);
2003 // write out all shapes
2004 writeShapes(aStatePool,
2005 aStateMap,
2006 xDocElem,
2007 m_xDocumentHandler);
2009 m_xDocumentHandler->endElement( USTR("draw:page") );
2010 m_xDocumentHandler->endElement( USTR("office:drawing") );
2011 m_xDocumentHandler->endElement( USTR("office:body") );
2012 m_xDocumentHandler->endElement( USTR("office:document") );
2013 m_xDocumentHandler->endDocument();
2015 return sal_True;
2018 ///////////////////////////////////////////////////////////////
2020 struct ShapeRenderingVisitor
2022 ShapeRenderingVisitor(StatePool& /*rStatePool*/,
2023 StateMap& rStateMap,
2024 OutputDevice& rOutDev,
2025 const std::vector< Gradient >& rGradientVector,
2026 const std::vector< GradientStop >& rGradientStopVector) :
2027 mrStateMap(rStateMap),
2028 mrOutDev(rOutDev),
2029 mrGradientVector(rGradientVector),
2030 mrGradientStopVector(rGradientStopVector)
2033 void operator()( const uno::Reference<xml::dom::XElement>& )
2037 void operator()( const uno::Reference<xml::dom::XElement>& xElem,
2038 const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
2040 sal_Int32 nDummyIndex(0);
2041 rtl::OUString sStyleId(
2042 xElem->getAttribute(
2043 USTR("internal-style-ref")).getToken(
2044 0,'$',nDummyIndex));
2045 StateMap::iterator pOrigState=mrStateMap.find(
2046 sStyleId.toInt32());
2048 if( pOrigState == mrStateMap.end() )
2049 return; // non-exportable element, e.g. linearGradient
2051 maCurrState = pOrigState->second;
2053 const sal_Int32 nTokenId(getTokenId(xElem->getNodeName()));
2054 switch(nTokenId)
2056 case XML_LINE:
2058 // collect attributes
2059 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2060 rtl::OUString sAttributeValue;
2061 double x1=0.0,y1=0.0,x2=0.0,y2=0.0;
2062 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2064 sAttributeValue = xAttributes->item(i)->getNodeValue();
2065 const sal_Int32 nAttribId(
2066 getTokenId(xAttributes->item(i)->getNodeName()));
2067 switch(nAttribId)
2069 case XML_X1:
2070 x1= convLength(sAttributeValue,maCurrState,'h');
2071 break;
2072 case XML_X2:
2073 x2 = convLength(sAttributeValue,maCurrState,'h');
2074 break;
2075 case XML_Y1:
2076 y1 = convLength(sAttributeValue,maCurrState,'v');
2077 break;
2078 case XML_Y2:
2079 y2 = convLength(sAttributeValue,maCurrState,'v');
2080 break;
2081 default:
2082 // skip
2083 break;
2087 basegfx::B2DPolygon aPoly;
2088 aPoly.append(basegfx::B2DPoint(x1,y1));
2089 aPoly.append(basegfx::B2DPoint(x2,y2));
2091 renderPathShape(basegfx::B2DPolyPolygon(aPoly));
2092 break;
2094 case XML_POLYGON:
2095 case XML_POLYLINE:
2097 rtl::OUString sPoints = xElem->hasAttribute(USTR("points")) ? xElem->getAttribute(USTR("points")) : USTR("");
2098 basegfx::B2DPolygon aPoly;
2099 basegfx::tools::importFromSvgPoints(aPoly, sPoints);
2100 if( nTokenId == XML_POLYGON || maCurrState.meFillType != NONE )
2101 aPoly.setClosed(true);
2103 renderPathShape(basegfx::B2DPolyPolygon(aPoly));
2104 break;
2106 case XML_RECT:
2108 // collect attributes
2109 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2110 rtl::OUString sAttributeValue;
2111 bool bRxSeen=false, bRySeen=false;
2112 double x=0.0,y=0.0,width=0.0,height=0.0,rx=0.0,ry=0.0;
2113 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2115 sAttributeValue = xAttributes->item(i)->getNodeValue();
2116 const sal_Int32 nAttribId(
2117 getTokenId(xAttributes->item(i)->getNodeName()));
2118 switch(nAttribId)
2120 case XML_X:
2121 x = convLength(sAttributeValue,maCurrState,'h');
2122 break;
2123 case XML_Y:
2124 y = convLength(sAttributeValue,maCurrState,'v');
2125 break;
2126 case XML_WIDTH:
2127 width = convLength(sAttributeValue,maCurrState,'h');
2128 break;
2129 case XML_HEIGHT:
2130 height = convLength(sAttributeValue,maCurrState,'v');
2131 break;
2132 case XML_RX:
2133 rx = convLength(sAttributeValue,maCurrState,'h');
2134 bRxSeen=true;
2135 break;
2136 case XML_RY:
2137 ry = convLength(sAttributeValue,maCurrState,'v');
2138 bRySeen=true;
2139 break;
2140 default:
2141 // skip
2142 break;
2146 if( bRxSeen && !bRySeen )
2147 ry = rx;
2148 else if( bRySeen && !bRxSeen )
2149 rx = ry;
2151 basegfx::B2DPolygon aPoly;
2152 aPoly = basegfx::tools::createPolygonFromRect(
2153 basegfx::B2DRange(x,y,x+width,y+height),
2154 rx, ry );
2156 renderPathShape(basegfx::B2DPolyPolygon(aPoly));
2157 break;
2159 case XML_PATH:
2161 rtl::OUString sPath = xElem->hasAttribute(USTR("d")) ? xElem->getAttribute(USTR("d")) : USTR("");
2162 basegfx::B2DPolyPolygon aPoly;
2163 basegfx::tools::importFromSvgD(aPoly, sPath);
2165 renderPathShape(aPoly);
2166 break;
2168 case XML_CIRCLE:
2170 // collect attributes
2171 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2172 rtl::OUString sAttributeValue;
2173 double cx=0.0,cy=0.0,r=0.0;
2174 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2176 sAttributeValue = xAttributes->item(i)->getNodeValue();
2177 const sal_Int32 nAttribId(
2178 getTokenId(xAttributes->item(i)->getNodeName()));
2179 switch(nAttribId)
2181 case XML_CX:
2182 cx = convLength(sAttributeValue,maCurrState,'h');
2183 break;
2184 case XML_CY:
2185 cy = convLength(sAttributeValue,maCurrState,'v');
2186 break;
2187 case XML_R:
2188 r = convLength(sAttributeValue,maCurrState,'o');
2189 default:
2190 // skip
2191 break;
2195 basegfx::B2DEllipse aEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r));
2196 basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(
2197 aEllipse.getB2DEllipseCenter(),
2198 aEllipse.getB2DEllipseRadius().getX(),
2199 aEllipse.getB2DEllipseRadius().getY());
2201 renderPathShape(basegfx::B2DPolyPolygon(aPoly));
2202 break;
2204 case XML_ELLIPSE:
2206 // collect attributes
2207 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2208 rtl::OUString sAttributeValue;
2209 double cx=0.0,cy=0.0,rx=0.0, ry=0.0;
2210 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2212 sAttributeValue = xAttributes->item(i)->getNodeValue();
2213 const sal_Int32 nAttribId(
2214 getTokenId(xAttributes->item(i)->getNodeName()));
2215 switch(nAttribId)
2217 case XML_CX:
2218 cx = convLength(sAttributeValue,maCurrState,'h');
2219 break;
2220 case XML_CY:
2221 cy = convLength(sAttributeValue,maCurrState,'v');
2222 break;
2223 case XML_RX:
2224 rx = convLength(sAttributeValue,maCurrState,'h');
2225 break;
2226 case XML_RY:
2227 ry = convLength(sAttributeValue,maCurrState,'v');
2228 default:
2229 // skip
2230 break;
2234 basegfx::B2DEllipse aEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry));
2235 basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(
2236 aEllipse.getB2DEllipseCenter(),
2237 aEllipse.getB2DEllipseRadius().getX(),
2238 aEllipse.getB2DEllipseRadius().getY());
2240 renderPathShape(basegfx::B2DPolyPolygon(aPoly));
2241 break;
2243 case XML_IMAGE:
2245 // collect attributes
2246 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2247 rtl::OUString sAttributeValue;
2248 double x=0.0,y=0.0,width=0.0,height=0.0;
2249 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2251 sAttributeValue = xAttributes->item(i)->getNodeValue();
2252 const sal_Int32 nAttribId(
2253 getTokenId(xAttributes->item(i)->getNodeName()));
2254 switch(nAttribId)
2256 case XML_X:
2257 x = convLength(sAttributeValue,maCurrState,'h');
2258 break;
2259 case XML_Y:
2260 y = convLength(sAttributeValue,maCurrState,'v');
2261 break;
2262 case XML_WIDTH:
2263 width = convLength(sAttributeValue,maCurrState,'h');
2264 break;
2265 case XML_HEIGHT:
2266 height = convLength(sAttributeValue,maCurrState,'v');
2267 break;
2268 default:
2269 // skip
2270 break;
2274 rtl::OUString sValue = xElem->hasAttribute(USTR("href")) ? xElem->getAttribute(USTR("href")) : USTR("");
2275 rtl::OString aValueUtf8( sValue.getStr(), sValue.getLength(), RTL_TEXTENCODING_UTF8 );
2276 std::string sLinkValue;
2277 parseXlinkHref(aValueUtf8.getStr(), sLinkValue);
2279 if (!sLinkValue.empty())
2281 // <- blatant copy from svx/source/xml/xmlgrhlp.cxx
2282 Graphic aGraphic;
2284 uno::Sequence<sal_Int8> aData;
2285 SvXMLUnitConverter::decodeBase64(aData,
2286 rtl::OUString::createFromAscii(sLinkValue.c_str()));
2287 SvMemoryStream aSrc(aData.getArray(),
2288 aData.getLength(),
2289 STREAM_READ);
2290 USHORT nFormat = GRFILTER_FORMAT_DONTKNOW;
2291 USHORT pDeterminedFormat = GRFILTER_FORMAT_DONTKNOW;
2292 GetGrfFilter()->ImportGraphic( aGraphic, String(), aSrc ,nFormat,&pDeterminedFormat );
2294 if (pDeterminedFormat == GRFILTER_FORMAT_DONTKNOW)
2296 //Read the first two byte to check whether it is a gzipped stream, is so it may be in wmz or emz format
2297 //unzip them and try again
2299 BYTE sFirstBytes[ 2 ];
2301 aSrc.Seek( STREAM_SEEK_TO_END );
2302 ULONG nStreamLen = aSrc.Tell();
2303 aSrc.Seek( 0 );
2305 if ( !nStreamLen )
2307 SvLockBytes* pLockBytes = aSrc.GetLockBytes();
2308 if ( pLockBytes )
2309 pLockBytes->SetSynchronMode( TRUE );
2311 aSrc.Seek( STREAM_SEEK_TO_END );
2312 nStreamLen = aSrc.Tell();
2313 aSrc.Seek( 0 );
2315 if( nStreamLen >= 2 )
2317 //read two byte
2318 aSrc.Read( sFirstBytes, 2 );
2320 if( sFirstBytes[0] == 0x1f && sFirstBytes[1] == 0x8b )
2322 SvMemoryStream* pDest = new SvMemoryStream;
2323 ZCodec aZCodec( 0x8000, 0x8000 );
2324 aZCodec.BeginCompression(ZCODEC_GZ_LIB);
2325 aSrc.Seek( 0 );
2326 aZCodec.Decompress( aSrc, *pDest );
2328 if (aZCodec.EndCompression() && pDest )
2330 pDest->Seek( STREAM_SEEK_TO_END );
2331 ULONG nStreamLen_ = pDest->Tell();
2332 if (nStreamLen_)
2334 pDest->Seek(0L);
2335 GetGrfFilter()->ImportGraphic( aGraphic, String(), *pDest ,nFormat,&pDeterminedFormat );
2338 delete pDest;
2342 // -> blatant copy from svx/source/xml/xmlgrhlp.cxx
2344 const Rectangle aBounds(
2345 Point(basegfx::fround(pt100thmm(x)),
2346 basegfx::fround(pt100thmm(y))),
2347 Size(basegfx::fround(pt100thmm(width)),
2348 basegfx::fround(pt100thmm(height))));
2349 aGraphic.Draw(&mrOutDev,
2350 aBounds.TopLeft(),
2351 aBounds.GetSize());
2352 maBounds.Union(aBounds);
2354 break;
2356 case XML_TEXT:
2358 // collect text from all TEXT_NODE children into sText
2359 rtl::OUStringBuffer sText;
2360 visitChildren(boost::bind(
2361 (rtl::OUStringBuffer& (rtl::OUStringBuffer::*)(const sal_Unicode* str))&rtl::OUStringBuffer::append,
2362 boost::ref(sText),
2363 boost::bind(&xml::dom::XNode::getNodeValue,
2364 _1)),
2365 xElem,
2366 xml::dom::NodeType_TEXT_NODE);
2368 // collect attributes
2369 const sal_Int32 nNumAttrs( xAttributes->getLength() );
2370 rtl::OUString sAttributeValue;
2371 double x=0.0,y=0.0,width=0.0,height=0.0;
2372 for( sal_Int32 i=0; i<nNumAttrs; ++i )
2374 sAttributeValue = xAttributes->item(i)->getNodeValue();
2375 const sal_Int32 nAttribId(
2376 getTokenId(xAttributes->item(i)->getNodeName()));
2377 switch(nAttribId)
2379 case XML_X:
2380 x = convLength(sAttributeValue,maCurrState,'h');
2381 break;
2382 case XML_Y:
2383 y = convLength(sAttributeValue,maCurrState,'v');
2384 break;
2385 case XML_WIDTH:
2386 width = convLength(sAttributeValue,maCurrState,'h');
2387 break;
2388 case XML_HEIGHT:
2389 height = convLength(sAttributeValue,maCurrState,'v');
2390 break;
2391 default:
2392 // skip
2393 break;
2397 // actually export text
2398 Font aFont(maCurrState.maFontFamily,
2399 Size(0,
2400 basegfx::fround(pt100thmm(maCurrState.mnFontSize))));
2401 aFont.SetAlign(ALIGN_BASELINE);
2402 aFont.SetColor(getVclColor(maCurrState.maFillColor));
2403 aFont.SetFillColor(getVclColor(maCurrState.maFillColor));
2405 if( !maCurrState.maFontStyle.equalsAscii("normal") )
2406 aFont.SetItalic(ITALIC_NORMAL); // TODO: discriminate
2407 if( !maCurrState.mnFontWeight != 400.0 )
2408 aFont.SetWeight(WEIGHT_BOLD); // TODO: discriminate
2410 // extract basic transformations out of CTM
2411 basegfx::B2DTuple aScale, aTranslate;
2412 double fRotate, fShearX;
2413 ::rtl::OUString sTransformValue;
2414 if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
2416 rtl::OUString sTransform;
2417 x += aTranslate.getX();
2418 y += aTranslate.getY();
2420 aFont.SetSize(
2421 Size(basegfx::fround(aFont.GetWidth()*aScale.getX()),
2422 basegfx::fround(aFont.GetHeight()*aScale.getY())));
2424 if( fRotate )
2425 aFont.SetOrientation(basegfx::fround(fRotate*1800.0/M_PI));
2428 // TODO(F2): update bounds
2429 mrOutDev.SetFont(aFont);
2430 const ::rtl::OUString aText( sText.makeStringAndClear() );
2431 switch( maCurrState.meTextAnchor )
2433 default:
2434 case BEFORE:
2435 break;
2437 case CENTER:
2439 const long nWidth=mrOutDev.GetTextWidth(aText);
2440 x -= nWidth/2;
2442 break;
2444 case AFTER:
2445 x -= mrOutDev.GetTextWidth(aText);
2446 break;
2448 mrOutDev.DrawText(Point(basegfx::fround(pt100thmm(x)),
2449 basegfx::fround(pt100thmm(y))),
2450 aText);
2451 break;
2456 void push()
2460 void pop()
2464 bool hasGradientOpacity( const Gradient& rGradient )
2466 return
2467 mrGradientStopVector[
2468 rGradient.maStops[0]].maStopColor.a != 1.0 ||
2469 mrGradientStopVector[
2470 rGradient.maStops[1]].maStopColor.a != 1.0;
2473 sal_Int8 toByteColor( double val )
2475 // TODO(Q3): duplicated from vcl::unotools
2476 return sal::static_int_cast<sal_Int8>(
2477 basegfx::fround(val*255.0));
2480 ::Color getVclColor( const ARGBColor& rColor )
2482 const sal_uInt8 nRed ( toByteColor(rColor.r) );
2483 const sal_uInt8 nGreen( toByteColor(rColor.g) );
2484 const sal_uInt8 nBlue ( toByteColor(rColor.b) );
2486 return ::Color(nRed,nGreen,nBlue);
2489 void renderPathShape(const basegfx::B2DPolyPolygon& rPoly)
2491 // we might need to split up polypolygon into multiple path
2492 // shapes (e.g. when emulating line stroking)
2493 State aState = maCurrState;
2495 // bring polygon from pt coordinate system to 100th millimeter
2496 aState.maCTM.scale(2540.0/72.0,2540.0/72.0);
2498 basegfx::B2DPolyPolygon aPoly(rPoly);
2499 aPoly.transform(aState.maCTM);
2501 const basegfx::B2DRange aBounds=basegfx::tools::getRange(aPoly);
2502 maBounds.Union(
2503 Rectangle(
2504 basegfx::fround(aBounds.getMinX()),
2505 basegfx::fround(aBounds.getMinY()),
2506 basegfx::fround(aBounds.getMaxX()),
2507 basegfx::fround(aBounds.getMaxY())));
2509 // fill first
2510 mrOutDev.SetLineColor();
2512 // do we have a gradient fill?
2513 if( aState.meFillType == GRADIENT && aState.maFillGradient.maStops.size() > 1 )
2515 ::Gradient aGradient;
2517 if( aState.maFillGradient.meType == Gradient::LINEAR )
2519 // should the optimizeGradientStops method decide that
2520 // this is a three-color gradient, it prolly wanted us
2521 // to take axial instead
2522 aGradient = ::Gradient( aState.maFillGradient.maStops.size() == 3 ?
2523 GRADIENT_AXIAL :
2524 GRADIENT_LINEAR );
2526 else
2528 aGradient = ::Gradient( GRADIENT_ELLIPTICAL );
2531 basegfx::B2DTuple rScale, rTranslate;
2532 double rRotate, rShearX;
2533 if( aState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
2534 aGradient.SetAngle( basegfx::fround(rRotate*1800.0/M_PI) );
2535 aGradient.SetStartColor( getVclColor(
2536 mrGradientStopVector[
2537 aState.maFillGradient.maStops[0]].maStopColor) );
2538 aGradient.SetEndColor( getVclColor(
2539 mrGradientStopVector[
2540 aState.maFillGradient.maStops[1]].maStopColor) );
2542 if( hasGradientOpacity(aState.maFillGradient) )
2544 ::Gradient aTransparencyGradient=aGradient;
2546 const BYTE cTransStart( 255-
2547 basegfx::fround(mrGradientStopVector[
2548 aState.maFillGradient.maStops[1]].maStopColor.a*
2549 aState.mnFillOpacity*maCurrState.mnOpacity*255.0));
2550 const Color aTransStart( cTransStart, cTransStart, cTransStart );
2552 const BYTE cTransEnd( 255-
2553 basegfx::fround(mrGradientStopVector[
2554 aState.maFillGradient.maStops[0]].maStopColor.a*
2555 aState.mnFillOpacity*maCurrState.mnOpacity*255.0));
2556 const Color aTransEnd( cTransEnd, cTransEnd, cTransEnd );
2558 // modulate gradient opacity with overall fill opacity
2559 aTransparencyGradient.SetStartColor(aTransStart);
2560 aTransparencyGradient.SetEndColor(aTransEnd);
2562 VirtualDevice aVDev;
2563 GDIMetaFile aMtf;
2565 aVDev.EnableOutput( FALSE );
2566 aVDev.SetMapMode( mrOutDev.GetMapMode() );
2567 aMtf.Record( &aVDev );
2569 aVDev.SetLineColor();
2570 aVDev.SetFillColor();
2571 aVDev.DrawGradient(::PolyPolygon(aPoly),aGradient);
2573 const Rectangle aMtfBounds(
2574 basegfx::fround(aBounds.getMinX()),
2575 basegfx::fround(aBounds.getMinY()),
2576 basegfx::fround(aBounds.getMaxX()),
2577 basegfx::fround(aBounds.getMaxY()));
2579 MapMode aMap(mrOutDev.GetMapMode());
2580 aMtf.Stop();
2581 aMtf.WindStart();
2582 aMap.SetOrigin( aMtfBounds.TopLeft() );
2583 aMtf.SetPrefMapMode( aMap );
2584 aMtf.SetPrefSize( aMtfBounds.GetSize() );
2586 mrOutDev.DrawTransparent(aMtf,
2587 aMtfBounds.TopLeft(),
2588 aMtfBounds.GetSize(),
2589 aTransparencyGradient);
2591 else
2593 mrOutDev.DrawGradient(::PolyPolygon(aPoly),aGradient);
2596 else
2598 if( aState.meFillType == NONE )
2599 mrOutDev.SetFillColor();
2600 else
2601 mrOutDev.SetFillColor(getVclColor(aState.maFillColor));
2603 if( aState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
2604 mrOutDev.DrawTransparent(::PolyPolygon(aPoly),
2605 basegfx::fround(
2606 (1.0-(aState.mnFillOpacity*maCurrState.mnOpacity))*100.0));
2607 else
2608 mrOutDev.DrawPolyPolygon(::PolyPolygon(aPoly));
2611 // Stroking now
2612 mrOutDev.SetFillColor();
2614 if( aState.meStrokeType != NONE &&
2615 (aState.maDashArray.size() ||
2616 aState.mnStrokeWidth != 1.0) )
2618 // vcl thick lines are severly borked - generate filled
2619 // polygon instead
2620 std::vector<basegfx::B2DPolyPolygon> aPolys;
2621 aPoly = rPoly;
2622 if( !aState.maDashArray.empty() )
2624 aPoly.clear();
2625 basegfx::B2DPolyPolygon aSegment;
2626 for( sal_uInt32 i=0; i<rPoly.count(); ++i )
2628 basegfx::tools::applyLineDashing(rPoly,
2629 aState.maDashArray,
2630 &aSegment);
2631 aPoly.append(aSegment);
2635 // applied line dashing to original rPoly above, to get
2636 // correctly transformed lengths - need to transform
2637 // again, now
2638 aPoly.transform(aState.maCTM);
2640 for( sal_uInt32 i=0; i<aPoly.count(); ++i )
2642 // ugly. convert to integer-based tools polygon
2643 // first, and only _then_ remove intersections (we
2644 // might get new ones from the rounding)
2645 aPolys.push_back(
2646 basegfx::tools::stripNeutralPolygons(
2647 basegfx::tools::prepareForPolygonOperation(
2648 ::PolyPolygon(
2649 basegfx::tools::createAreaGeometry(
2650 aPoly.getB2DPolygon(i),
2651 pt100thmm(aState.mnStrokeWidth/2.0),
2652 aState.meLineJoin)).getB2DPolyPolygon())));
2653 // TODO(F2): line ends
2656 mrOutDev.SetLineColor();
2657 mrOutDev.SetFillColor(getVclColor(aState.maStrokeColor));
2659 for( sal_uInt32 i=0; i<aPolys.size(); ++i )
2661 if( aState.mnStrokeOpacity*maCurrState.mnOpacity != 1.0 )
2662 mrOutDev.DrawTransparent(::PolyPolygon(aPolys[i]),
2663 basegfx::fround(
2664 (1.0-(aState.mnStrokeOpacity*maCurrState.mnOpacity))*100.0));
2665 else
2666 mrOutDev.DrawPolyPolygon(::PolyPolygon(aPolys[i]));
2668 const basegfx::B2DRange aStrokeBounds=basegfx::tools::getRange(aPolys[i]);
2669 maBounds.Union(
2670 Rectangle(
2671 basegfx::fround(aStrokeBounds.getMinX()),
2672 basegfx::fround(aStrokeBounds.getMinY()),
2673 basegfx::fround(aStrokeBounds.getMaxX()),
2674 basegfx::fround(aStrokeBounds.getMaxY())));
2677 else
2679 if( aState.meStrokeType == NONE )
2680 mrOutDev.SetLineColor();
2681 else
2682 mrOutDev.SetLineColor(getVclColor(aState.maStrokeColor));
2684 if( aState.mnStrokeOpacity*maCurrState.mnOpacity != 1.0 )
2685 mrOutDev.DrawTransparent(::PolyPolygon(aPoly),
2686 basegfx::fround(
2687 (1.0-(aState.mnStrokeOpacity*maCurrState.mnOpacity))*100.0));
2688 else
2689 mrOutDev.DrawPolyPolygon(::PolyPolygon(aPoly));
2693 State maCurrState;
2694 StateMap& mrStateMap;
2695 OutputDevice& mrOutDev;
2696 const std::vector< Gradient >& mrGradientVector;
2697 const std::vector< GradientStop >& mrGradientStopVector;
2698 Rectangle maBounds;
2701 } // namespace svgi
2703 bool importSvg(SvStream & rStream, Graphic & rGraphic )
2705 const uno::Reference<lang::XMultiServiceFactory> xServiceFactory(
2706 ::comphelper::getProcessServiceFactory());
2708 uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(
2709 xServiceFactory->createInstance(
2710 rtl::OUString::createFromAscii("com.sun.star.xml.dom.DocumentBuilder")),
2711 uno::UNO_QUERY );
2713 uno::Reference<io::XInputStream> xStream(
2714 new utl::OInputStreamWrapper(rStream) );
2716 uno::Reference<xml::dom::XDocument> xDom(
2717 xDomBuilder->parse(xStream),
2718 uno::UNO_QUERY_THROW );
2720 uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(),
2721 uno::UNO_QUERY_THROW );
2723 VirtualDevice aVDev;
2724 GDIMetaFile aMtf;
2726 aVDev.EnableOutput( FALSE );
2727 aMtf.Record( &aVDev );
2728 aVDev.SetTextAlign(ALIGN_BASELINE);
2730 // parse styles and fill state stack
2731 svgi::State aInitialState;
2732 svgi::StatePool aStatePool;
2733 svgi::StateMap aStateMap;
2734 svgi::AnnotatingVisitor aVisitor(aStatePool,
2735 aStateMap,
2736 aInitialState,
2737 uno::Reference<xml::sax::XDocumentHandler>());
2738 svgi::visitElements(aVisitor, xDocElem);
2740 #ifdef VERBOSE
2741 dumpTree(xDocElem);
2742 #endif
2744 // render all shapes to mtf
2745 svgi::ShapeRenderingVisitor aRenderer(aStatePool,aStateMap,aVDev,
2746 aVisitor.maGradientVector,
2747 aVisitor.maGradientStopVector);
2748 svgi::visitElements(aRenderer, xDocElem);
2750 aMtf.Stop();
2752 aMtf.WindStart();
2753 aMtf.SetPrefMapMode( MAP_100TH_MM );
2755 // get the document dimensions
2757 // if the "width" and "height" attributes are missing, inkscape fakes
2758 // A4 portrait for. Let's do the same.
2759 if (!xDocElem->hasAttribute(USTR("width")))
2760 xDocElem->setAttribute(USTR("width"), USTR("210mm"));
2761 if (!xDocElem->hasAttribute(USTR("height")))
2762 xDocElem->setAttribute(USTR("height"), USTR("297mm"));
2764 aMtf.SetPrefSize(
2765 Size(
2766 std::max(
2767 sal_Int32(aRenderer.maBounds.Right()),
2768 basegfx::fround( svgi::pt100thmm(svgi::convLength(xDocElem->getAttribute(USTR("width")),aInitialState,'h')) )),
2769 std::max(
2770 sal_Int32(aRenderer.maBounds.Bottom()),
2771 basegfx::fround( svgi::pt100thmm(svgi::convLength(xDocElem->getAttribute(USTR("height")),aInitialState,'v')) ))));
2773 rGraphic = aMtf;
2775 return sal_True;