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