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