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