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