ooo-build r12703 - in trunk: . patches/dev300
- From: thorstenb svn gnome org
- To: svn-commits-list gnome org
- Subject: ooo-build r12703 - in trunk: . patches/dev300
- Date: Thu, 29 May 2008 14:33:41 +0000 (UTC)
Author: thorstenb
Date: Thu May 29 14:33:40 2008
New Revision: 12703
URL: http://svn.gnome.org/viewvc/ooo-build?rev=12703&view=rev
Log:
* patches/dev300/svg-import-basegfx-m10.diff
* patches/dev300/svg-import-basegfx.diff: fix wrong bracket
placement, leading to incorrect elliptical arc import
* patches/dev300/svg-import-filter.diff: added gradient & text
support, fixed a bunch of parser bugs
* patches/dev300/svg-import-filter-gfxfilter.diff
* patches/dev300/apply: added a graphic filter for svg with
similar capabilities as the doc import above
Added:
trunk/patches/dev300/svg-import-filter-gfxfilter.diff
Modified:
trunk/ChangeLog
trunk/patches/dev300/apply
trunk/patches/dev300/svg-import-basegfx-m10.diff
trunk/patches/dev300/svg-import-basegfx.diff
trunk/patches/dev300/svg-import-filter.diff
Modified: trunk/patches/dev300/apply
==============================================================================
--- trunk/patches/dev300/apply (original)
+++ trunk/patches/dev300/apply Thu May 29 14:33:40 2008
@@ -1950,6 +1950,8 @@
# Marco Cecchetti's work on importing text
#svg-import-text.diff, jholesov
+# A bit less partial implementation of SVG import
+# Work in progress, but fairly usable already
svg-import-filter.diff, thorsten
[ SVGImport < dev300-m11 ]
@@ -1958,6 +1960,9 @@
[ SVGImport >= dev300-m11 ]
svg-import-basegfx.diff, thorsten
+# like svg-import-filter.diff, but adds a graphic filter for SVG
+svg-import-filter-gfxfilter.diff, thorsten
+
[ CalcDataPilotDrillDown < dev300-m14 ]
# Implements DataPilot cache table, result drill-down, and some extra UNO API.
SectionOwner => kohei
Modified: trunk/patches/dev300/svg-import-basegfx-m10.diff
==============================================================================
--- trunk/patches/dev300/svg-import-basegfx-m10.diff (original)
+++ trunk/patches/dev300/svg-import-basegfx-m10.diff Thu May 29 14:33:40 2008
@@ -225,7 +225,7 @@
+
+
+ const double fFactor(
-+ (bLargeArcFlag==bSweepFlag) ? -1.0 : 1.0 *
++ (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) *
+ sqrt((fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/
+ (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX())));
+
Modified: trunk/patches/dev300/svg-import-basegfx.diff
==============================================================================
--- trunk/patches/dev300/svg-import-basegfx.diff (original)
+++ trunk/patches/dev300/svg-import-basegfx.diff Thu May 29 14:33:40 2008
@@ -103,7 +103,7 @@
void lcl_skipNumber(sal_Int32& io_rPos,
const ::rtl::OUString& rStr,
const sal_Int32 nLen)
-@@ -621,24 +654,185 @@ namespace basegfx
+@@ -623,24 +656,185 @@
break;
}
@@ -226,7 +226,7 @@
+
+
+ const double fFactor(
-+ (bLargeArcFlag==bSweepFlag) ? -1.0 : 1.0 *
++ (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) *
+ sqrt((fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/
+ (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX())));
+
@@ -300,7 +300,7 @@
}
break;
}
-@@ -667,7 +861,33 @@ namespace basegfx
+@@ -667,6 +861,32 @@ namespace basegfx
return true;
}
Added: trunk/patches/dev300/svg-import-filter-gfxfilter.diff
==============================================================================
--- (empty file)
+++ trunk/patches/dev300/svg-import-filter-gfxfilter.diff Thu May 29 14:33:40 2008
@@ -0,0 +1,872 @@
+diff -ur filter_orig/source/config/fragments/internalgraphicfilters/svg_Export.xcu filter/source/config/fragments/internalgraphicfilters/svg_Export.xcu
+--- filter_orig/source/config/fragments/internalgraphicfilters/svg_Export.xcu 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/config/fragments/internalgraphicfilters/svg_Export.xcu 2008-05-28 02:30:15.000000000 +0200
+@@ -8,3 +8,13 @@
+ </prop>
+ <prop oor:name="Flags"><value>EXPORT</value></prop>
+ </node>
++ <node oor:name="svg_Import" oor:op="replace" >
++ <prop oor:name="Type"><value>svg_Scalable_Vector_Graphics</value></prop>
++ <prop oor:name="FormatName"><value>svgfilter</value></prop>
++ <prop oor:name="RealFilterName"/>
++ <prop oor:name="UIComponent"/>
++ <prop oor:name="UIName">
++ <value xml:lang="en-US">SVG - Scalable Vector Graphics</value>
++ </prop>
++ <prop oor:name="Flags"><value>IMPORT</value></prop>
++ </node>
+diff -ur filter_orig/source/svg/exports.map filter/source/svg/exports.map
+--- filter_orig/source/svg/exports.map 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/svg/exports.map 2008-05-27 23:32:34.000000000 +0200
+@@ -3,6 +3,7 @@
+ component_getImplementationEnvironment;
+ component_getFactory;
+ component_writeInfo;
++ GraphicImport;
+
+ local:
+ *;
+diff -ur filter_orig/source/svg/makefile.mk filter/source/svg/makefile.mk
+--- filter_orig/source/svg/makefile.mk 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/svg/makefile.mk 2008-05-28 02:16:23.000000000 +0200
+@@ -78,6 +78,7 @@
+ $(UNOTOOLSLIB) \
+ $(TOOLSLIB) \
+ $(COMPHELPERLIB) \
++ $(SVTOOLLIB) \
+ $(CPPUHELPERLIB) \
+ $(CPPULIB) \
+ $(SALLIB) \
+diff -ur filter_orig/source/svg/svgfilter.cxx filter/source/svg/svgfilter.cxx
+--- filter_orig/source/svg/svgfilter.cxx 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/svg/svgfilter.cxx 2008-05-27 23:32:59.000000000 +0200
+@@ -243,3 +243,11 @@
+ {
+ return SVGFilter_getSupportedServiceNames();
+ }
++
++// -----------------------------------------------------------------------------
++
++class FilterConfigItem;
++extern "C" BOOL __LOADONCALLAPI GraphicImport(SvStream & rStream, Graphic & rGraphic, FilterConfigItem*, BOOL )
++{
++ return importSvg( rStream, rGraphic );
++}
+diff -ur filter_orig/source/svg/svgfilter.hxx filter/source/svg/svgfilter.hxx
+--- filter_orig/source/svg/svgfilter.hxx 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/svg/svgfilter.hxx 2008-05-27 23:03:42.000000000 +0200
+@@ -328,4 +328,11 @@
+ SAL_CALL SVGFilter_createInstance( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > & rSMgr)
+ throw ( ::com::sun::star::uno::Exception );
+
++// -----------------------------------------------------------------------------
++
++class SvStream;
++class Graphic;
++
++bool importSvg(SvStream & rStream, Graphic & rGraphic );
++
+ #endif // SVGFILTER_HXX
+diff -ur filter_orig/source/svg/svgreader.cxx filter/source/svg/svgreader.cxx
+--- filter_orig/source/svg/svgreader.cxx 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/svg/svgreader.cxx 2008-05-28 22:12:21.000000000 +0200
+@@ -7,6 +7,7 @@
+ * Thorsten Behrens <tbehrens novell com>
+ *
+ * Copyright (C) 2008, Novell Inc.
++ * Parts copyright 2005 by Sun Microsystems, Inc.
+ *
+ * The Contents of this file are made available subject to
+ * the terms of GNU Lesser General Public License Version 2.1.
+@@ -41,6 +42,17 @@
+ #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+ #include <com/sun/star/xml/dom/NodeType.hpp>
+
++#include <comphelper/processfactory.hxx>
++#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
++#include <basegfx/polygon/b2dpolypolygontools.hxx>
++#include <unotools/streamwrap.hxx>
++#include <xmloff/xmluconv.hxx>
++#include <vcl/graph.hxx>
++#include <vcl/virdev.hxx>
++#include <vcl/gradient.hxx>
++#include <svx/impgrf.hxx>
++#include <tools/zcodec.hxx>
++
+ #include <boost/bind.hpp>
+ #include <hash_set>
+ #include <map>
+@@ -432,6 +444,9 @@
+ // start&end color)
+ optimizeGradientStops(rState.maFillGradient);
+
++ if( !mxDocumentHandler.is() )
++ return true; // cannot write style, svm import case
++
+ // do we have a gradient fill? then write out gradient as well
+ if( rState.meFillType == GRADIENT && rState.maFillGradient.maStops.size() > 1 )
+ {
+@@ -1541,10 +1556,13 @@
+ for( sal_uInt32 i=0; i<rPoly.count(); ++i )
+ {
+ aPolys.push_back(
+- basegfx::tools::createAreaGeometryForPolygon(
+- rPoly.getB2DPolygon(i),
+- aState.mnStrokeWidth/2.0,
+- aState.meLineJoin));
++ basegfx::tools::removeNeutralPolygons(
++ basegfx::tools::removeAllIntersections(
++ basegfx::tools::createAreaGeometryForPolygon(
++ rPoly.getB2DPolygon(i),
++ aState.mnStrokeWidth/2.0,
++ aState.meLineJoin)),
++ true));
+ // TODO(F2): line ends
+ }
+
+@@ -1896,4 +1914,734 @@
+ return sal_True;
+ }
+
++///////////////////////////////////////////////////////////////
++
++struct ShapeRenderingVisitor
++{
++ ShapeRenderingVisitor(StatePool& rStatePool,
++ StateMap& rStateMap,
++ OutputDevice& rOutDev,
++ const std::vector< Gradient >& rGradientVector,
++ const std::vector< GradientStop >& rGradientStopVector) :
++ mrStates(rStatePool),
++ mrStateMap(rStateMap),
++ mrOutDev(rOutDev),
++ mrGradientVector(rGradientVector),
++ mrGradientStopVector(rGradientStopVector)
++ {}
++
++ void operator()( const uno::Reference<xml::dom::XElement>& )
++ {
++ }
++
++ void operator()( const uno::Reference<xml::dom::XElement>& xElem,
++ const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
++ {
++ sal_Int32 nDummyIndex(0);
++ rtl::OUString sStyleId(
++ xElem->getAttribute(
++ USTR("internal-style-ref")).getToken(
++ 0,'$',nDummyIndex));
++ StateMap::iterator pOrigState=mrStateMap.find(
++ sStyleId.toInt32());
++ maCurrState = pOrigState != mrStateMap.end() ? pOrigState->second : maParentStates.back();
++
++ OSL_TRACE("the CTM is now #2: %f %f %f %f %f %f",
++ maCurrState.maCTM.get(0,0),
++ maCurrState.maCTM.get(0,1),
++ maCurrState.maCTM.get(0,2),
++ maCurrState.maCTM.get(1,0),
++ maCurrState.maCTM.get(1,1),
++ maCurrState.maCTM.get(1,2));
++
++ const sal_Int32 nTokenId(getTokenId(xElem->getNodeName()));
++ switch(nTokenId)
++ {
++ case XML_LINE:
++ {
++ // collect attributes
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ double x1=0.0,y1=0.0,x2=0.0,y2=0.0;
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ sAttributeValue = xAttributes->item(i)->getNodeValue();
++ const sal_Int32 nAttribId(
++ getTokenId(xAttributes->item(i)->getNodeName()));
++ switch(nAttribId)
++ {
++ case XML_X1:
++ x1= convLength(sAttributeValue);
++ break;
++ case XML_X2:
++ x2 = convLength(sAttributeValue);
++ break;
++ case XML_Y1:
++ y1 = convLength(sAttributeValue);
++ break;
++ case XML_Y2:
++ y2 = convLength(sAttributeValue);
++ break;
++ default:
++ // skip
++ break;
++ }
++ }
++
++ basegfx::B2DPolygon aPoly;
++ aPoly.append(basegfx::B2DPoint(x1,y1));
++ aPoly.append(basegfx::B2DPoint(x2,y2));
++
++ renderPathShape(basegfx::B2DPolyPolygon(aPoly));
++ break;
++ }
++ case XML_POLYGON:
++ case XML_POLYLINE:
++ {
++ rtl::OUString sPoints = xElem->hasAttribute(USTR("points")) ? xElem->getAttribute(USTR("points")) : USTR("");
++ basegfx::B2DPolygon aPoly;
++ basegfx::tools::importFromSvgPoints(aPoly, sPoints);
++ // if( nTokenId == XML_POLYGON )
++ aPoly.setClosed(true);
++
++ // assuming the coordinates are in pts since they are interpreted as such by different renderers
++ basegfx::B2DHomMatrix aScale;
++ aScale.scale(2540.0f/72.0f,2540.0f/72.0f);
++ aPoly.transform(aScale);
++
++ renderPathShape(basegfx::B2DPolyPolygon(aPoly));
++ break;
++ }
++ case XML_RECT:
++ {
++ // collect attributes
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ bool bRxSeen=false, bRySeen=false;
++ double x=0.0,y=0.0,width=0.0,height=0.0,rx=0.0,ry=0.0;
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ sAttributeValue = xAttributes->item(i)->getNodeValue();
++ const sal_Int32 nAttribId(
++ getTokenId(xAttributes->item(i)->getNodeName()));
++ switch(nAttribId)
++ {
++ case XML_X:
++ x = convLength(sAttributeValue);
++ break;
++ case XML_Y:
++ y = convLength(sAttributeValue);
++ break;
++ case XML_WIDTH:
++ width = convLength(sAttributeValue);
++ break;
++ case XML_HEIGHT:
++ height = convLength(sAttributeValue);
++ break;
++ case XML_RX:
++ rx = convLength(sAttributeValue);
++ bRxSeen=true;
++ break;
++ case XML_RY:
++ ry = convLength(sAttributeValue);
++ bRySeen=true;
++ break;
++ default:
++ // skip
++ break;
++ }
++ }
++
++ if( bRxSeen && !bRySeen )
++ ry = rx;
++ else if( bRySeen && !bRxSeen )
++ rx = ry;
++
++ basegfx::B2DPolygon aPoly;
++ aPoly = basegfx::tools::createPolygonFromRect(
++ basegfx::B2DRange(x,y,x+width,y+height),
++ rx, ry );
++
++ renderPathShape(basegfx::B2DPolyPolygon(aPoly));
++ break;
++ }
++ case XML_PATH:
++ {
++ rtl::OUString sPath = xElem->hasAttribute(USTR("d")) ? xElem->getAttribute(USTR("d")) : USTR("");
++ basegfx::B2DPolyPolygon aPoly;
++ basegfx::tools::importFromSvgD(aPoly, sPath);
++
++ // assuming the coordinates are in pts since they are interpreted as such by different renderers
++ basegfx::B2DHomMatrix aScale;
++ aScale.scale(2540.0f/72.0f,2540.0f/72.0f);
++ aPoly.transform(aScale);
++
++ renderPathShape(aPoly);
++ break;
++ }
++ case XML_CIRCLE:
++ {
++ // collect attributes
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ double cx=0.0,cy=0.0,r=0.0;
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ sAttributeValue = xAttributes->item(i)->getNodeValue();
++ const sal_Int32 nAttribId(
++ getTokenId(xAttributes->item(i)->getNodeName()));
++ switch(nAttribId)
++ {
++ case XML_CX:
++ cx = convLength(sAttributeValue);
++ break;
++ case XML_CY:
++ cy = convLength(sAttributeValue);
++ break;
++ case XML_R:
++ r = convLength(sAttributeValue);
++ default:
++ // skip
++ break;
++ }
++ }
++
++ basegfx::B2DEllipse aEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r));
++ basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(
++ aEllipse.getB2DEllipseCenter(),
++ aEllipse.getB2DEllipseRadius().getX(),
++ aEllipse.getB2DEllipseRadius().getY());
++
++ renderPathShape(basegfx::B2DPolyPolygon(aPoly));
++ break;
++ }
++ case XML_ELLIPSE:
++ {
++ // collect attributes
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ double cx=0.0,cy=0.0,rx=0.0, ry=0.0;
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ sAttributeValue = xAttributes->item(i)->getNodeValue();
++ const sal_Int32 nAttribId(
++ getTokenId(xAttributes->item(i)->getNodeName()));
++ switch(nAttribId)
++ {
++ case XML_CX:
++ cx = convLength(sAttributeValue);
++ break;
++ case XML_CY:
++ cy = convLength(sAttributeValue);
++ break;
++ case XML_RX:
++ rx = convLength(sAttributeValue);
++ break;
++ case XML_RY:
++ ry = convLength(sAttributeValue);
++ default:
++ // skip
++ break;
++ }
++ }
++
++ basegfx::B2DEllipse aEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry));
++ basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(
++ aEllipse.getB2DEllipseCenter(),
++ aEllipse.getB2DEllipseRadius().getX(),
++ aEllipse.getB2DEllipseRadius().getY());
++
++ renderPathShape(basegfx::B2DPolyPolygon(aPoly));
++ break;
++ }
++ case XML_IMAGE:
++ {
++ // collect attributes
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ double x=0.0,y=0.0,width=0.0,height=0.0;
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ sAttributeValue = xAttributes->item(i)->getNodeValue();
++ const sal_Int32 nAttribId(
++ getTokenId(xAttributes->item(i)->getNodeName()));
++ switch(nAttribId)
++ {
++ case XML_X:
++ x = convLength(sAttributeValue);
++ break;
++ case XML_Y:
++ y = convLength(sAttributeValue);
++ break;
++ case XML_WIDTH:
++ width = convLength(sAttributeValue);
++ break;
++ case XML_HEIGHT:
++ height = convLength(sAttributeValue);
++ break;
++ default:
++ // skip
++ break;
++ }
++ }
++
++ rtl::OUString sValue = xElem->hasAttribute(USTR("href")) ? xElem->getAttribute(USTR("href")) : USTR("");
++ rtl::OString aValueUtf8( sValue.getStr(), sValue.getLength(), RTL_TEXTENCODING_UTF8 );
++ std::string sLinkValue;
++ parseXlinkHref(aValueUtf8.getStr(), sLinkValue);
++
++ if (!sLinkValue.empty())
++ {
++ // <- blatant copy from svx/source/xml/xmlgrhlp.cxx
++ Graphic aGraphic;
++
++ uno::Sequence<sal_Int8> aData;
++ SvXMLUnitConverter::decodeBase64(aData,
++ rtl::OUString::createFromAscii(sLinkValue.c_str()));
++ SvMemoryStream aSrc(aData.getArray(),
++ aData.getLength(),
++ STREAM_READ);
++ USHORT nFormat = GRFILTER_FORMAT_DONTKNOW;
++ USHORT pDeterminedFormat = GRFILTER_FORMAT_DONTKNOW;
++ GetGrfFilter()->ImportGraphic( aGraphic, String(), aSrc ,nFormat,&pDeterminedFormat );
++
++ if (pDeterminedFormat == GRFILTER_FORMAT_DONTKNOW)
++ {
++ //Read the first two byte to check whether it is a gzipped stream, is so it may be in wmz or emz format
++ //unzip them and try again
++
++ BYTE sFirstBytes[ 2 ];
++
++ aSrc.Seek( STREAM_SEEK_TO_END );
++ ULONG nStreamLen = aSrc.Tell();
++ aSrc.Seek( 0 );
++
++ if ( !nStreamLen )
++ {
++ SvLockBytes* pLockBytes = aSrc.GetLockBytes();
++ if ( pLockBytes )
++ pLockBytes->SetSynchronMode( TRUE );
++
++ aSrc.Seek( STREAM_SEEK_TO_END );
++ nStreamLen = aSrc.Tell();
++ aSrc.Seek( 0 );
++ }
++ if( nStreamLen >= 2 )
++ {
++ //read two byte
++ aSrc.Read( sFirstBytes, 2 );
++
++ if( sFirstBytes[0] == 0x1f && sFirstBytes[1] == 0x8b )
++ {
++ SvMemoryStream* pDest = new SvMemoryStream;
++ ZCodec aZCodec( 0x8000, 0x8000 );
++ aZCodec.BeginCompression(ZCODEC_GZ_LIB);
++ aSrc.Seek( 0 );
++ aZCodec.Decompress( aSrc, *pDest );
++
++ if (aZCodec.EndCompression() && pDest )
++ {
++ pDest->Seek( STREAM_SEEK_TO_END );
++ ULONG nStreamLen_ = pDest->Tell();
++ if (nStreamLen_)
++ {
++ pDest->Seek(0L);
++ GetGrfFilter()->ImportGraphic( aGraphic, String(), *pDest ,nFormat,&pDeterminedFormat );
++ }
++ }
++ delete pDest;
++ }
++ }
++ }
++ // -> blatant copy from svx/source/xml/xmlgrhlp.cxx
++
++ const Rectangle aBounds(
++ Point(basegfx::fround(x),
++ basegfx::fround(y)),
++ Size(basegfx::fround(width),
++ basegfx::fround(height)));
++ aGraphic.Draw(&mrOutDev,
++ aBounds.TopLeft(),
++ aBounds.GetSize());
++ maBounds.Union(aBounds);
++ }
++ break;
++ }
++ case XML_TEXT:
++ {
++ // collect text from all TEXT_NODE children into sText
++ rtl::OUStringBuffer sText;
++ visitChildren(boost::bind(
++ (rtl::OUStringBuffer& (rtl::OUStringBuffer::*)(const sal_Unicode* str))&rtl::OUStringBuffer::append,
++ boost::ref(sText),
++ boost::bind(&xml::dom::XNode::getNodeValue,
++ _1)),
++ xElem,
++ xml::dom::NodeType_TEXT_NODE);
++
++ // collect attributes
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ double x=0.0,y=0.0,width=0.0,height=0.0;
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ sAttributeValue = xAttributes->item(i)->getNodeValue();
++ const sal_Int32 nAttribId(
++ getTokenId(xAttributes->item(i)->getNodeName()));
++ switch(nAttribId)
++ {
++ case XML_X:
++ x = convLength(sAttributeValue);
++ break;
++ case XML_Y:
++ y = convLength(sAttributeValue);
++ break;
++ case XML_WIDTH:
++ width = convLength(sAttributeValue);
++ break;
++ case XML_HEIGHT:
++ height = convLength(sAttributeValue);
++ break;
++ default:
++ // skip
++ break;
++ }
++ }
++
++ // actually export text
++ Font aFont(maCurrState.maFontFamily,
++ Size(0,
++ basegfx::fround(maCurrState.mnFontSize)));
++
++ // extract basic transformations out of CTM
++ basegfx::B2DTuple aScale, aTranslate;
++ double fRotate, fShearX;
++ ::rtl::OUString sTransformValue;
++ if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
++ {
++ rtl::OUString sTransform;
++ x += aTranslate.getX();
++ y += aTranslate.getY();
++
++ aFont.SetSize(
++ Size(basegfx::fround(aFont.GetWidth()*aScale.getX()),
++ basegfx::fround(aFont.GetHeight()*aScale.getY())));
++
++ if( fRotate )
++ aFont.SetOrientation(basegfx::fround(fRotate*1800.0/M_PI));
++ }
++
++ // TODO(F2): update bounds
++ mrOutDev.SetFont(aFont);
++ mrOutDev.DrawText(Point(basegfx::fround(x),
++ basegfx::fround(y)),
++ sText.makeStringAndClear());
++ break;
++ }
++ }
++ }
++
++ void push()
++ {
++ maParentStates.push_back(maCurrState);
++ }
++
++ void pop()
++ {
++ maParentStates.pop_back();
++ }
++
++ bool hasGradientOpacity( const Gradient& rGradient )
++ {
++ return
++ mrGradientStopVector[
++ rGradient.maStops[0]].maStopColor.a != 1.0 ||
++ mrGradientStopVector[
++ rGradient.maStops[1]].maStopColor.a != 1.0;
++ }
++
++ sal_Int8 toByteColor( double val )
++ {
++ // TODO(Q3): duplicated from vcl::unotools
++ return sal::static_int_cast<sal_Int8>(
++ basegfx::fround(val*255.0));
++ }
++
++ ::Color getVclColor( const ARGBColor& rColor )
++ {
++ const sal_uInt8 nRed ( toByteColor(rColor.r) );
++ const sal_uInt8 nGreen( toByteColor(rColor.g) );
++ const sal_uInt8 nBlue ( toByteColor(rColor.b) );
++
++ return ::Color(nRed,nGreen,nBlue);
++ }
++
++ void renderPathShape(const basegfx::B2DPolyPolygon& rPoly)
++ {
++ // we might need to split up polypolygon into multiple path
++ // shapes (e.g. when emulating line stroking)
++ State aState = maCurrState;
++
++ basegfx::B2DPolyPolygon aPoly(rPoly);
++ aPoly.transform(aState.maCTM);
++
++ OSL_TRACE("the CTM is now #2: %f %f %f %f %f %f",
++ maCurrState.maCTM.get(0,0),
++ maCurrState.maCTM.get(0,1),
++ maCurrState.maCTM.get(0,2),
++ maCurrState.maCTM.get(1,0),
++ maCurrState.maCTM.get(1,1),
++ maCurrState.maCTM.get(1,2));
++
++ const basegfx::B2DRange aBounds=basegfx::tools::getRange(aPoly);
++ maBounds.Union(
++ Rectangle(
++ basegfx::fround(aBounds.getMinX()),
++ basegfx::fround(aBounds.getMinY()),
++ basegfx::fround(aBounds.getMaxX()),
++ basegfx::fround(aBounds.getMaxY())));
++
++ // fill first
++ mrOutDev.SetLineColor();
++
++ // do we have a gradient fill?
++ if( aState.meFillType == GRADIENT && aState.maFillGradient.maStops.size() > 1 )
++ {
++ ::Gradient aGradient;
++
++ if( aState.maFillGradient.meType == Gradient::LINEAR )
++ {
++ // should the optimizeGradientStops method decide that
++ // this is a three-color gradient, it prolly wanted us
++ // to take axial instead
++ aGradient = ::Gradient( aState.maFillGradient.maStops.size() == 3 ?
++ GRADIENT_AXIAL :
++ GRADIENT_LINEAR );
++ }
++ else
++ {
++ aGradient = ::Gradient( GRADIENT_ELLIPTICAL );
++ }
++
++ basegfx::B2DTuple rScale, rTranslate;
++ double rRotate, rShearX;
++ if( aState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
++ aGradient.SetAngle( basegfx::fround(rRotate*1800.0/M_PI) );
++ aGradient.SetStartColor( getVclColor(
++ mrGradientStopVector[
++ aState.maFillGradient.maStops[0]].maStopColor) );
++ aGradient.SetEndColor( getVclColor(
++ mrGradientStopVector[
++ aState.maFillGradient.maStops[1]].maStopColor) );
++
++ if( hasGradientOpacity(aState.maFillGradient) )
++ {
++ ::Gradient aTransparencyGradient=aGradient;
++
++ const BYTE cTransStart( 255-
++ basegfx::fround(mrGradientStopVector[
++ aState.maFillGradient.maStops[1]].maStopColor.a*
++ aState.mnFillOpacity*255.0));
++ const Color aTransStart( cTransStart, cTransStart, cTransStart );
++
++ const BYTE cTransEnd( 255-
++ basegfx::fround(mrGradientStopVector[
++ aState.maFillGradient.maStops[0]].maStopColor.a*
++ aState.mnFillOpacity*255.0));
++ const Color aTransEnd( cTransEnd, cTransEnd, cTransEnd );
++
++ // modulate gradient opacity with overall fill opacity
++ aTransparencyGradient.SetStartColor(aTransStart);
++ aTransparencyGradient.SetEndColor(aTransEnd);
++
++ VirtualDevice aVDev;
++ GDIMetaFile aMtf;
++
++ aVDev.EnableOutput( FALSE );
++ aMtf.Record( &aVDev );
++ aVDev.DrawGradient(::PolyPolygon(aPoly),aGradient);
++
++ aMtf.Stop();
++ aMtf.WindStart();
++ aMtf.SetPrefMapMode( MAP_100TH_MM );
++ aMtf.SetPrefSize( Size(basegfx::fround(aBounds.getMaxX()),
++ basegfx::fround(aBounds.getMaxY())) );
++
++ mrOutDev.DrawTransparent(aMtf,
++ Point(),
++ aMtf.GetPrefSize(),
++ aTransparencyGradient);
++ }
++ else
++ {
++ mrOutDev.DrawGradient(::PolyPolygon(aPoly),aGradient);
++ }
++ }
++ else
++ {
++ if( aState.meFillType == NONE )
++ mrOutDev.SetFillColor();
++ else
++ mrOutDev.SetFillColor(getVclColor(aState.maFillColor));
++
++ if( aState.mnFillOpacity != 1.0 )
++ mrOutDev.DrawTransparent(::PolyPolygon(aPoly),
++ basegfx::fround(
++ (1.0-aState.mnFillOpacity)*100.0));
++ else
++ mrOutDev.DrawPolyPolygon(::PolyPolygon(aPoly));
++ }
++
++ // Stroking now
++ mrOutDev.SetFillColor();
++
++ if( aState.meStrokeType != NONE &&
++ (aState.maDashArray.size() ||
++ aState.mnStrokeWidth != 1.0) )
++ {
++ // vcl thick lines are severly borked - generate filled
++ // polygon instead
++ std::vector<basegfx::B2DPolyPolygon> aPolys;
++ aPoly=basegfx::tools::adaptiveSubdivideByAngle(aPoly);
++ if( !aState.maDashArray.empty() )
++ aPoly=basegfx::tools::applyLineDashing(aPoly,
++ aState.maDashArray);
++ for( sal_uInt32 i=0; i<aPoly.count(); ++i )
++ {
++ // ugly. convert to integer-based tools polygon
++ // first, and only _then_ remove intersections (we
++ // might get new ones from the rounding)
++ aPolys.push_back(
++ basegfx::tools::removeNeutralPolygons(
++ basegfx::tools::removeAllIntersections(
++ ::PolyPolygon(
++ basegfx::tools::createAreaGeometryForPolygon(
++ aPoly.getB2DPolygon(i),
++ aState.mnStrokeWidth/2.0,
++ aState.meLineJoin)).getB2DPolyPolygon()),
++ true));
++ // TODO(F2): line ends
++ }
++
++ mrOutDev.SetLineColor();
++ mrOutDev.SetFillColor(getVclColor(aState.maStrokeColor));
++
++ for( sal_uInt32 i=0; i<aPolys.size(); ++i )
++ {
++ if( aState.mnStrokeOpacity != 1.0 )
++ mrOutDev.DrawTransparent(::PolyPolygon(aPolys[i]),
++ basegfx::fround(
++ (1.0-aState.mnStrokeOpacity)*100.0));
++ else
++ mrOutDev.DrawPolyPolygon(::PolyPolygon(aPolys[i]));
++
++ const basegfx::B2DRange aStrokeBounds=basegfx::tools::getRange(aPolys[i]);
++ maBounds.Union(
++ Rectangle(
++ basegfx::fround(aStrokeBounds.getMinX()),
++ basegfx::fround(aStrokeBounds.getMinY()),
++ basegfx::fround(aStrokeBounds.getMaxX()),
++ basegfx::fround(aStrokeBounds.getMaxY())));
++ }
++ }
++ else
++ {
++ if( aState.meStrokeType == NONE )
++ mrOutDev.SetLineColor();
++ else
++ mrOutDev.SetLineColor(getVclColor(aState.maStrokeColor));
++
++ if( aState.mnStrokeOpacity != 1.0 )
++ mrOutDev.DrawTransparent(::PolyPolygon(aPoly),
++ basegfx::fround(
++ (1.0-aState.mnStrokeOpacity)*100.0));
++ else
++ mrOutDev.DrawPolyPolygon(::PolyPolygon(aPoly));
++ }
++ }
++
++ State maCurrState;
++ std::vector<State> maParentStates;
++ StatePool& mrStates;
++ StateMap& mrStateMap;
++ OutputDevice& mrOutDev;
++ const std::vector< Gradient >& mrGradientVector;
++ const std::vector< GradientStop >& mrGradientStopVector;
++ Rectangle maBounds;
++};
++
+ } // namespace svgi
++
++bool importSvg(SvStream & rStream, Graphic & rGraphic )
++{
++ const uno::Reference<lang::XMultiServiceFactory> xServiceFactory(
++ ::comphelper::getProcessServiceFactory());
++
++ uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(
++ xServiceFactory->createInstance(
++ rtl::OUString::createFromAscii("com.sun.star.xml.dom.DocumentBuilder")),
++ uno::UNO_QUERY );
++
++ uno::Reference<io::XInputStream> xStream(
++ new utl::OInputStreamWrapper(rStream) );
++
++ uno::Reference<xml::dom::XDocument> xDom(
++ xDomBuilder->parse(xStream),
++ uno::UNO_QUERY_THROW );
++
++ uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(),
++ uno::UNO_QUERY_THROW );
++
++ VirtualDevice aVDev;
++ GDIMetaFile aMtf;
++
++ aVDev.EnableOutput( FALSE );
++ aMtf.Record( &aVDev );
++
++ // parse styles and fill state stack
++ svgi::StatePool aStatePool;
++ svgi::StateMap aStateMap;
++ svgi::AnnotatingVisitor aVisitor(aStatePool,
++ aStateMap,
++ uno::Reference<xml::sax::XDocumentHandler>());
++ svgi::visitElements(aVisitor, xDocElem);
++
++#ifdef VERBOSE
++ dumpTree(xDocElem);
++#endif
++
++ // render all shapes to mtf
++ svgi::ShapeRenderingVisitor aRenderer(aStatePool,aStateMap,aVDev,
++ aVisitor.maGradientVector,
++ aVisitor.maGradientStopVector);
++ svgi::visitElements(aRenderer, xDocElem);
++
++ aMtf.Stop();
++
++ aMtf.WindStart();
++ aMtf.SetPrefMapMode( MAP_100TH_MM );
++
++ // get the document dimensions
++
++ // if the "width" and "height" attributes are missing, inkscape fakes
++ // A4 portrait for. Let's do the same.
++ if (!xDocElem->hasAttribute(USTR("width")))
++ xDocElem->setAttribute(USTR("width"), USTR("210mm"));
++ if (!xDocElem->hasAttribute(USTR("height")))
++ xDocElem->setAttribute(USTR("height"), USTR("297mm"));
++
++ aMtf.SetPrefSize(
++ Size(
++ std::max(
++ sal_Int32(aRenderer.maBounds.Right()),
++ basegfx::fround(svgi::convLength(xDocElem->getAttribute(USTR("width"))))),
++ std::max(
++ sal_Int32(aRenderer.maBounds.Bottom()),
++ basegfx::fround(svgi::convLength(xDocElem->getAttribute(USTR("height"))))) ));
++
++ rGraphic = aMtf;
++
++ return sal_True;
++}
++
++
+diff -ur filter_orig/source/svg/test/makefile.mk filter/source/svg/test/makefile.mk
+--- filter_orig/source/svg/test/makefile.mk 2008-05-28 02:34:02.000000000 +0200
++++ filter/source/svg/test/makefile.mk 2008-05-28 02:18:07.000000000 +0200
+@@ -40,6 +40,7 @@
+ $(UNOTOOLSLIB) \
+ $(TOOLSLIB) \
+ $(COMPHELPERLIB) \
++ $(SVTOOLLIB) \
+ $(CPPUHELPERLIB) \
+ $(CPPULIB) \
+ $(SALLIB) \
Modified: trunk/patches/dev300/svg-import-filter.diff
==============================================================================
--- trunk/patches/dev300/svg-import-filter.diff (original)
+++ trunk/patches/dev300/svg-import-filter.diff Thu May 29 14:33:40 2008
@@ -343,7 +343,7 @@
+close ( GPERF );
--- filter/source/svg/gfxtypes.hxx 1970-01-01 01:00:00.000000000 +0100
+++ filter/source/svg/gfxtypes.hxx 2008-04-11 00:09:55.000000000 +0200
-@@ -0,0 +1,317 @@
+@@ -0,0 +1,356 @@
+/*************************************************************************
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
@@ -377,7 +377,7 @@
+{
+ double toDoubleColor( sal_uInt8 val ) { return val/255.0; }
+
-+ ARGBColor() : a(0.0), r(0.0), g(0.0), b(0.0)
++ ARGBColor() : a(1.0), r(0.0), g(0.0), b(0.0)
+ {}
+ explicit ARGBColor(double fGrey) : a(1.0), r(fGrey), g(fGrey), b(fGrey)
+ {}
@@ -406,33 +406,72 @@
+};
+inline bool operator==( const ARGBColor& rLHS, const ARGBColor& rRHS )
+{ return rLHS.a==rRHS.a && rLHS.r==rRHS.r && rLHS.g==rRHS.g && rLHS.b==rRHS.b; }
++inline bool operator!=( const ARGBColor& rLHS, const ARGBColor& rRHS )
++{ return !(rLHS==rRHS); }
+
+struct GradientStop
+{
-+ GradientStop() : maStopColor(), mnStopPosition(0.0), msId()
++ GradientStop() : maStopColor(), mnStopPosition(0.0)
+ {}
+ ARGBColor maStopColor;
+ double mnStopPosition;
-+ rtl::OUString msId;
+};
+inline bool operator==( const GradientStop& rLHS, const GradientStop& rRHS )
+{ return rLHS.mnStopPosition==rRHS.mnStopPosition && rLHS.maStopColor==rRHS.maStopColor; }
+
+struct Gradient
+{
-+ Gradient() : maStops(), mbBoundingBoxUnits(false), msId(), msLinkedTo(), mfX1(0.0f), mfX2(0.0f), mfY1(0.0f), mfY2(0.0f)
-+ {}
-+ std::vector<GradientStop> maStops;
-+ bool mbBoundingBoxUnits;
-+ rtl::OUString msId, msLinkedTo;
-+ double mfX1, mfX2, mfY1, mfY2;
++ enum GradientType { LINEAR, RADIAL};
++ std::vector<sal_Size> maStops;
++ basegfx::B2DHomMatrix maTransform;
++ GradientType meType;
++ union
++ {
++ double test;
++ struct
++ {
++ double mfX1;
++ double mfX2;
++ double mfY1;
++ double mfY2;
++ } linear;
++ struct
++ {
++ double mfCX;
++ double mfCY;
++ double mfFX;
++ double mfFY;
++ double mfR;
++ } radial;
++ } maCoords;
++ sal_Int32 mnId;
++ bool mbBoundingBoxUnits;
++ bool mbLinearBoundingBoxUnits;
++
++// explicit Gradient(GradientType eType) : maStops(), maTransform(), meType(eType), maCoords.mfCX(0.0), maCoords.mfCY(0.0), maCoords.mfFX(0.0), maCoords.mfFY(0.0), maCoords.mfR(0.0), mnId(0), mbBoundingBoxUnits(false)
++ explicit Gradient(GradientType eType) : maStops(), maTransform(), meType(eType), mnId(0), mbBoundingBoxUnits(false)
++ {
++ maCoords.radial.mfCX = 0.0;
++ maCoords.radial.mfCY = 0.0;
++ maCoords.radial.mfFX = 0.0;
++ maCoords.radial.mfFY = 0.0;
++ maCoords.radial.mfR = 0.0;
++ }
+};
+
+inline bool operator==( const Gradient& rLHS, const Gradient& rRHS )
+{
-+ return rLHS.mbBoundingBoxUnits==rRHS.mbBoundingBoxUnits && rLHS.maStops==rRHS.maStops &&
-+ rLHS.msId.equals(rRHS.msId) && rLHS.msLinkedTo.equals(rRHS.msLinkedTo) &&
-+ rLHS.mfX1 == rRHS.mfX1 && rLHS.mfX2 == rRHS.mfX2 && rLHS.mfY1 == rRHS.mfY1 && rLHS.mfY2 == rRHS.mfY2;
++ if( rLHS.meType != rRHS.meType )
++ return false;
++ if( rLHS.meType == Gradient::LINEAR )
++ return rLHS.mbBoundingBoxUnits==rRHS.mbBoundingBoxUnits && rLHS.maStops==rRHS.maStops &&
++ rLHS.maCoords.linear.mfX1 == rRHS.maCoords.linear.mfX1 && rLHS.maCoords.linear.mfX2 == rRHS.maCoords.linear.mfX2 &&
++ rLHS.maCoords.linear.mfY1 == rRHS.maCoords.linear.mfY1 && rLHS.maCoords.linear.mfY2 == rRHS.maCoords.linear.mfY2;
++ else
++ return rLHS.mbBoundingBoxUnits==rRHS.mbBoundingBoxUnits && rLHS.maStops==rRHS.maStops &&
++ rLHS.maCoords.radial.mfCX == rRHS.maCoords.radial.mfCX && rLHS.maCoords.radial.mfCY == rRHS.maCoords.radial.mfCY &&
++ rLHS.maCoords.radial.mfFX == rRHS.maCoords.radial.mfFX && rLHS.maCoords.radial.mfFY == rRHS.maCoords.radial.mfFY &&
++ rLHS.maCoords.radial.mfR == rRHS.maCoords.radial.mfR;
+}
+
+enum PaintType
@@ -492,17 +531,17 @@
+ mnTextLineIncrement(0.0),
+ maCurrentColor(1.0),
+ mbVisibility(true),
-+ meFillType(NONE),
++ meFillType(SOLID),
+ mnFillOpacity(1.0),
+ meStrokeType(NONE),
+ mnStrokeOpacity(1.0),
+ meViewportFillType(NONE),
+ mnViewportFillOpacity(1.0),
+ maFillColor(0.0),
-+ maFillGradient(),
++ maFillGradient(Gradient::LINEAR),
+ meFillRule(NON_ZERO),
+ maStrokeColor(0.0),
-+ maStrokeGradient(),
++ maStrokeGradient(Gradient::LINEAR),
+ maDashArray(),
+ mnDashOffset(0.0),
+ meLineCap(BUTT),
@@ -510,7 +549,7 @@
+ mnMiterLimit(4.0),
+ mnStrokeWidth(1.0),
+ maViewportFillColor(1.0),
-+ maViewportFillGradient(),
++ maViewportFillGradient(Gradient::LINEAR),
+ mnStyleId(0)
+ {}
+
@@ -733,7 +772,7 @@
SHL1DEPN=
SHL1IMPLIB= i$(SHL1TARGET)
-@@ -87,3 +94,17 @@
+@@ -87,3 +94,16 @@
# --- Targets ----------------------------------
.INCLUDE : target.mk
@@ -750,10 +789,9 @@
+$(SLO)$/parserfragments.obj : $(INCCOM)$/tokens.cxx $(INCCOM)$/tokens.hxx
+
+$(SLO)$/svgreader.obj : $(INCCOM)$/tokens.cxx $(INCCOM)$/tokens.hxx
-+
--- filter/source/svg/parserfragments.cxx 1970-01-01 01:00:00.000000000 +0100
+++ filter/source/svg/parserfragments.cxx 2008-04-11 00:09:55.000000000 +0200
-@@ -0,0 +1,536 @@
+@@ -0,0 +1,553 @@
+/*************************************************************************
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
@@ -841,18 +879,23 @@
+ double fSkewAngle)
+{
+ geometry::AffineMatrix2D aMat(1.0,0.0,0.0,
-+ tan(fSkewAngle),1.0,0.0);
++ tan(fSkewAngle*M_PI/180),1.0,0.0);
+ rTransforms.push_back(aMat);
+}
+
+void calcSkewY(std::vector<geometry::AffineMatrix2D>& rTransforms,
+ double fSkewAngle)
+{
-+ geometry::AffineMatrix2D aMat(1.0,tan(fSkewAngle),0.0,
++ geometry::AffineMatrix2D aMat(1.0,tan(fSkewAngle*M_PI/180),0.0,
+ 0.0,1.0,0.0);
+ rTransforms.push_back(aMat);
+}
+
++void assign_twice(double& r_oVal1, double& r_oVal2, const double& rInVal )
++{
++ r_oVal1 = r_oVal2 = rInVal;
++}
++
+geometry::AffineMatrix2D multiplyMatrix( const geometry::AffineMatrix2D& rLHS,
+ const geometry::AffineMatrix2D& rRHS )
+{
@@ -896,6 +939,7 @@
+ // rgb() form
+ (str_p("rgb")
+ >> '(' >>
++ (
+ // rgb(int,int,int)
+ (byte_p[boost::bind(&setIntColor,
+ boost::ref(rColor.r),_1)] >> ',' >>
@@ -908,6 +952,7 @@
+ (real_p[assign_a(rColor.r)] >> ',' >>
+ real_p[assign_a(rColor.g)] >> ',' >>
+ real_p[assign_a(rColor.b)])
++ )
+ >> ')')
+ ) >> end_p,
+ // End grammar
@@ -918,7 +963,16 @@
+ }
+
+ // no free-form color - maybe a color name?
-+ switch (getTokenId(sColor, strlen(sColor)))
++ // trim white space before
++ while( *sColor &&
++ (*sColor==' ' || *sColor=='\t' || *sColor=='\r' || *sColor=='\n') )
++ ++sColor;
++ // trim white space after
++ int nLen=strlen(sColor)-1;
++ while( nLen &&
++ (sColor[nLen]==' ' || sColor[nLen]=='\t' || sColor[nLen]=='\r' || sColor[nLen]=='\n') )
++ --nLen;
++ switch (getTokenId(sColor, nLen+1))
+ {
+ case XML_ALICEBLUE: rColor = ARGBColor(240,248,255); return true;
+ case XML_ANTIQUEWHITE: rColor = ARGBColor(250,235,215); return true;
@@ -1077,14 +1131,13 @@
+{
+ using namespace ::boost::spirit;
+
-+
+ if( parse(sOpacity,
-+ // Begin grammar
-+ (
-+ real_p[assign_a(rColor.a)]
-+ ) >> end_p,
-+ // End grammar
-+ space_p).full )
++ // Begin grammar
++ (
++ real_p[assign_a(rColor.a)]
++ ) >> end_p,
++ // End grammar
++ space_p).full )
+ {
+ return true;
+ }
@@ -1119,7 +1172,7 @@
+ (str_p("ref")
+ >> '('
+ >> str_p("svg")[assign_a(bRefTransform,true)]
-+ >> !(real_p[assign_a(fRefOffsetX)] >> ',' >>
++ >> !(real_p[assign_a(fRefOffsetX)] >> (',' | eps_p) >>
+ real_p[assign_a(fRefOffsetY)])
+ >> ')')
+ |
@@ -1129,35 +1182,42 @@
+ // matrix(a,b,c,d,e,f)
+ (str_p("matrix")
+ >> '('
-+ >> real_p[assign_a(aCurrTransform.m00)] >> ','
-+ >> real_p[assign_a(aCurrTransform.m10)] >> ','
-+ >> real_p[assign_a(aCurrTransform.m01)] >> ','
-+ >> real_p[assign_a(aCurrTransform.m11)] >> ','
-+ >> real_p[assign_a(aCurrTransform.m02)] >> ','
++ >> real_p[assign_a(aCurrTransform.m00)] >> (',' | eps_p)
++ >> real_p[assign_a(aCurrTransform.m10)] >> (',' | eps_p)
++ >> real_p[assign_a(aCurrTransform.m01)] >> (',' | eps_p)
++ >> real_p[assign_a(aCurrTransform.m11)] >> (',' | eps_p)
++ >> real_p[assign_a(aCurrTransform.m02)] >> (',' | eps_p)
+ >> real_p[assign_a(aCurrTransform.m12)]
+ >> ')')[push_back_a(aTransforms,aCurrTransform)]
+ |
+ // translate(x,[y])
+ (str_p("translate")
+ >> '('
-+ >> real_p[assign_a(aCurrTransform.m02)]
-+ >> !(',' >> real_p[assign_a(aCurrTransform.m12)])
++ >> real_p[boost::bind(&assign_twice,
++ boost::ref(aCurrTransform.m02),
++ boost::ref(aCurrTransform.m12),_1)]
++ >> !((',' | eps_p) >> real_p[assign_a(aCurrTransform.m12)])
+ >> ')')[push_back_a(aTransforms,aCurrTransform)]
+ |
+ // scale(x,[y])
+ (str_p("scale")
+ >> '('
-+ >> real_p[assign_a(aCurrTransform.m00)]
-+ >> !(',' >> real_p[assign_a(aCurrTransform.m11)])
++ >> real_p[boost::bind(&assign_twice,
++ boost::ref(aCurrTransform.m00),
++ boost::ref(aCurrTransform.m11),_1)]
++ >> !((',' | eps_p) >> real_p[assign_a(aCurrTransform.m11)])
+ >> ')')[push_back_a(aTransforms,aCurrTransform)]
+ |
+ // rotate(phi,[cx, cy])
+ (str_p("rotate")
+ >> '('
-+ >> real_p[assign_a(fRotationAngle)] >> ')')[boost::bind(&calcRotation,
-+ boost::ref(aTransforms),
-+ boost::ref(aCurrTransform),
-+ boost::cref(fRotationAngle))]
++ >> real_p[assign_a(fRotationAngle)]
++ >> !((',' | eps_p) >> real_p[assign_a(aCurrTransform.m02)]
++ >> real_p[assign_a(aCurrTransform.m12)])
++ >> ')')[boost::bind(&calcRotation,
++ boost::ref(aTransforms),
++ boost::ref(aCurrTransform),
++ boost::cref(fRotationAngle))]
+ |
+ // skewX(phi)
+ (str_p("skewX")
@@ -1211,17 +1271,12 @@
+ const bool bRes = parse(sViewbox,
+ // Begin grammar
+ (
-+ // either comma- or space-delimited list of four doubles
-+ (real_p[assign_a(x)] >> ',' >>
-+ real_p[assign_a(y)] >> ',' >>
-+ real_p[assign_a(w)] >> ',' >>
-+ real_p[assign_a(h)])
-+ |
-+ (real_p[assign_a(x)] >>
-+ real_p[assign_a(y)] >>
-+ real_p[assign_a(w)] >>
-+ real_p[assign_a(h)])
-+ ) >> end_p,
++ // either comma- or space-delimited list of four doubles
++ real_p[assign_a(x)] >> (',' | eps_p) >>
++ real_p[assign_a(y)] >> (',' | eps_p) >>
++ real_p[assign_a(w)] >> (',' | eps_p) >>
++ real_p[assign_a(h)] >> end_p
++ ),
+ // End grammar
+ space_p).full;
+
@@ -1243,8 +1298,14 @@
+ return parse(sDashArray,
+ // Begin grammar
+ (
-+ // parse comma-delimited list of doubles
-+ list_p(real_p[push_back_a(rOutputVector)])
++ // parse comma-delimited list of doubles (have to use the
++ // 'direct' variant, as otherwise spirit refactors our
++ // parser to push both real num and comma to push_back_a)
++ list_p.direct
++ (
++ real_p[push_back_a(rOutputVector)],
++ ','
++ )
+ ) >> end_p,
+ // End grammar
+ space_p).full;
@@ -1281,12 +1342,6 @@
+ }
+
+ return false;
-+#if 0
-+ // the "normal" outplace uri
-+ (repeat_p(1, more)[anychar_p[boost::bind(&appendChar,
-+ boost::ref(data),
-+ _1)]])
-+#endif
+}
+
+} // namespace svgi
@@ -1392,7 +1447,7 @@
+ return assigner<Target,Value>(rTarget,aValue);
+ }
+
-+ template<typename Target> inline assign_actor<Target>
++ template <typename Target> inline assign_actor<Target>
+ assign_a(Target& rTarget)
+ {
+ return assign_actor<Target>(rTarget);
@@ -1507,7 +1562,7 @@
--- filter/source/svg/svgfilter.hxx 2008-04-11 08:49:31.000000000 +0200
+++ filter/source/svg/svgfilter.hxx 2008-04-11 00:09:55.000000000 +0200
-@@ -57,9 +57,7 @@
+@@ -38,20 +38,14 @@
#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
#include <com/sun/star/presentation/XPresentationSupplier.hpp>
#include <com/sun/star/document/XFilter.hpp>
@@ -1517,7 +1572,6 @@
#include <com/sun/star/document/XExporter.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
-@@ -80,11 +78,7 @@
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <cppuhelper/implbase1.hxx>
@@ -1788,7 +1842,7 @@
--- filter/source/svg/svgreader.cxx 1970-01-01 01:00:00.000000000 +0100
+++ filter/source/svg/svgreader.cxx 2008-04-11 09:33:30.000000000 +0200
-@@ -0,0 +1,1516 @@
+@@ -0,0 +1,1899 @@
+/*************************************************************************
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
@@ -1894,6 +1948,22 @@
+ rFunc.pop();
+}
+
++template<typename value_type> value_type square(value_type v)
++{
++ return v*v;
++}
++
++double colorDiffSquared(const ARGBColor& rCol1, const ARGBColor& rCol2)
++{
++ return
++ square(rCol1.a-rCol2.a)
++ + square(rCol1.r-rCol2.r)
++ + square(rCol1.g-rCol2.g)
++ + square(rCol1.b-rCol2.b);
++}
++
++typedef std::map<rtl::OUString,sal_Size> ElementRefMapType;
++
+struct AnnotatingVisitor
+{
+ AnnotatingVisitor(StatePool& rStatePool,
@@ -1905,9 +1975,8 @@
+ mrStates(rStatePool),
+ mrStateMap(rStateMap),
+ mxDocumentHandler(xDocumentHandler),
-+ mpCurrentGradient(NULL),
-+ mpCurrentGradientStop(NULL),
-+ mxGradientVector()
++ maGradientVector(),
++ maGradientStopVector()
+ {
+ State aState;
+ aState.maCTM = aState.maTransform;
@@ -1920,31 +1989,97 @@
+ void operator()( const uno::Reference<xml::dom::XElement>& xElem,
+ const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
+ {
-+ sal_Int32 nTagId(getTokenId(xElem->getTagName()));
++ const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
+ switch (nTagId)
+ {
+ case XML_LINEARGRADIENT:
+ {
+ const sal_Int32 nNumAttrs( xAttributes->getLength() );
+ rtl::OUString sAttributeValue;
-+ mxGradientVector.push_back(Gradient());
-+ mpCurrentGradient = &(mxGradientVector.back());
++ maGradientVector.push_back(Gradient(Gradient::LINEAR));
++
++ // do we have a reference to a parent gradient? parse
++ // that first, as it sets our defaults here (manually
++ // tracking default state on each Gradient variable is
++ // much more overhead)
++ uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem(USTR("href")));
++ if(xNode.is())
++ {
++ const rtl::OUString sValue(xNode->getNodeValue());
++ ElementRefMapType::iterator aFound=maGradientIdMap.end();
++ if (sValue.copy(0,1).equalsAscii("#"))
++ aFound = maGradientIdMap.find(sValue.copy(1));
++ else
++ aFound = maGradientIdMap.find(sValue);;
++
++ if( aFound != maGradientIdMap.end() )
++ maGradientVector.back() = maGradientVector[aFound->second];
++ }
++
++ // do that after dereferencing, to prevent hyperlinked
++ // gradient to clobber our Id again
++ maGradientVector.back().mnId = maGradientVector.size()-1;
++ maGradientVector.back().meType = Gradient::LINEAR; // has been clobbered as well
++
++ for( sal_Int32 i=0; i<nNumAttrs; ++i )
++ {
++ parseLinearGradientData( maGradientVector.back(),
++ maGradientVector.size()-1,
++ getTokenId(xAttributes->item(i)->getNodeName()),
++ xAttributes->item(i)->getNodeValue() );
++ }
++ break;
++ }
++ case XML_RADIALGRADIENT:
++ {
++ const sal_Int32 nNumAttrs( xAttributes->getLength() );
++ rtl::OUString sAttributeValue;
++ maGradientVector.push_back(Gradient(Gradient::RADIAL));
++
++ // do we have a reference to a parent gradient? parse
++ // that first, as it sets our defaults here (manually
++ // tracking default state on each Gradient variable is
++ // much more overhead)
++ uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem(USTR("href")));
++ if(xNode.is())
++ {
++ const rtl::OUString sValue(xNode->getNodeValue());
++ ElementRefMapType::iterator aFound=maGradientIdMap.end();
++ if (sValue.copy(0,1).equalsAscii("#"))
++ aFound = maGradientIdMap.find(sValue.copy(1));
++ else
++ aFound = maGradientIdMap.find(sValue);;
++
++ if( aFound != maGradientIdMap.end() )
++ maGradientVector.back() = maGradientVector[aFound->second];
++ }
++
++ // do that after dereferencing, to prevent hyperlinked
++ // gradient to clobber our Id again
++ maGradientVector.back().mnId = maGradientVector.size()-1;
++ maGradientVector.back().meType = Gradient::RADIAL; // has been clobbered as well
++
+ for( sal_Int32 i=0; i<nNumAttrs; ++i )
+ {
-+ parseGradientData( getTokenId(xAttributes->item(i)->getNodeName()), xAttributes->item(i)->getNodeValue() );
++ parseRadialGradientData( maGradientVector.back(),
++ maGradientVector.size()-1,
++ getTokenId(xAttributes->item(i)->getNodeName()),
++ xAttributes->item(i)->getNodeValue() );
+ }
+ break;
+ }
+ case XML_STOP:
+ {
-+
+ const sal_Int32 nNumAttrs( xAttributes->getLength() );
+ rtl::OUString sAttributeValue;
-+ mpCurrentGradient->maStops.push_back(GradientStop());
-+ mpCurrentGradientStop = &(mpCurrentGradient->maStops.back());
++ maGradientStopVector.push_back(GradientStop());
++ maGradientVector.back().maStops.push_back(maGradientStopVector.size()-1);
+ for( sal_Int32 i=0; i<nNumAttrs; ++i )
+ {
-+ parseGradientStop( getTokenId(xAttributes->item(i)->getNodeName()), xAttributes->item(i)->getNodeValue() );
++ parseGradientStop( maGradientStopVector.back(),
++ maGradientStopVector.size()-1,
++ getTokenId(xAttributes->item(i)->getNodeName()),
++ xAttributes->item(i)->getNodeValue() );
+ }
+ break;
+ }
@@ -2010,6 +2145,85 @@
+ return rtl::OUString::createFromAscii(sPrefix)+rtl::OUString::valueOf(nId);
+ }
+
++ bool hasGradientOpacity( const Gradient& rGradient )
++ {
++ return
++ maGradientStopVector[
++ rGradient.maStops[0]].maStopColor.a != 1.0 ||
++ maGradientStopVector[
++ rGradient.maStops[1]].maStopColor.a != 1.0;
++ }
++
++ struct StopSorter
++ {
++ explicit StopSorter( const std::vector< GradientStop >& rStopVec ) :
++ mrStopVec(rStopVec)
++ {}
++
++ bool operator()( sal_Size rLHS, sal_Size rRHS )
++ {
++ return mrStopVec[rLHS].mnStopPosition < mrStopVec[rRHS].mnStopPosition;
++ }
++
++ const std::vector< GradientStop >& mrStopVec;
++ };
++
++ void optimizeGradientStops( Gradient& rGradient )
++ {
++ // sort for increasing stop position
++ std::sort(rGradient.maStops.begin(),rGradient.maStops.end(),
++ StopSorter(maGradientStopVector));
++
++ if( rGradient.maStops.size() < 3 )
++ return; //easy! :-)
++
++ // join similar colors
++ std::vector<sal_Size> aNewStops(rGradient.maStops.front());
++ for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
++ {
++ if( maGradientStopVector[rGradient.maStops[i]].maStopColor !=
++ maGradientStopVector[aNewStops.back()].maStopColor )
++ aNewStops.push_back(rGradient.maStops[i]);
++ }
++
++ rGradient.maStops = aNewStops;
++
++ // axial gradient, maybe?
++ if( rGradient.meType == Gradient::LINEAR &&
++ rGradient.maStops.size() == 3 &&
++ maGradientStopVector[rGradient.maStops.front()].maStopColor ==
++ maGradientStopVector[rGradient.maStops.back()].maStopColor )
++ {
++ // yep - keep it at that
++ return;
++ }
++
++ // find out most significant color difference, and limit to
++ // those two stops around this border (metric is
++ // super-simplistic: take euclidean distance of colors, weigh
++ // with stop distance)
++ sal_Size nMaxIndex=0;
++ double fMaxDistance=0.0;
++ for( sal_Size i=1; i<rGradient.maStops.size(); ++i )
++ {
++ const double fCurrDistance(
++ colorDiffSquared(
++ maGradientStopVector[rGradient.maStops[i-1]].maStopColor,
++ maGradientStopVector[rGradient.maStops[i]].maStopColor) *
++ (square(maGradientStopVector[rGradient.maStops[i-1]].mnStopPosition) +
++ square(maGradientStopVector[rGradient.maStops[i]].mnStopPosition)) );
++
++ if( fCurrDistance > fMaxDistance )
++ {
++ nMaxIndex = i-1;
++ fMaxDistance = fCurrDistance;
++ }
++ }
++ rGradient.maStops[0] = rGradient.maStops[nMaxIndex];
++ rGradient.maStops[1] = rGradient.maStops[nMaxIndex+1];
++ rGradient.maStops.erase(rGradient.maStops.begin()+2,rGradient.maStops.end());
++ }
++
+ sal_Int8 toByteColor( double val )
+ {
+ // TODO(Q3): duplicated from vcl::unotools
@@ -2041,7 +2255,7 @@
+ return aBuf.makeStringAndClear();
+ }
+
-+ bool writeStyle(const State& rState, sal_Int32 nTagId)
++ bool writeStyle(State& rState, const sal_Int32 nTagId)
+ {
+ rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
+ uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );
@@ -2058,63 +2272,167 @@
+ mrStateMap.insert(std::make_pair(
+ mnCurrStateId,
+ rState));
-+
++
++ // find two representative stop colors (as odf only support
++ // start&end color)
++ optimizeGradientStops(rState.maFillGradient);
++
++ // do we have a gradient fill? then write out gradient as well
++ if( rState.meFillType == GRADIENT && rState.maFillGradient.maStops.size() > 1 )
++ {
++ // TODO(F3): ODF12 supposedly also groks svg:linear/radialGradient
++ xAttrs->AddAttribute( USTR( "draw:name" ), getStyleName("svggradient", rState.maFillGradient.mnId) );
++ if( rState.maFillGradient.meType == Gradient::LINEAR )
++ {
++ // should the optimizeGradientStops method decide that
++ // this is a three-color gradient, it prolly wanted us
++ // to take axial instead
++ xAttrs->AddAttribute( USTR( "draw:style" ),
++ rState.maFillGradient.maStops.size() == 3 ?
++ USTR("axial") :
++ USTR("linear") );
++ }
++ else
++ {
++ xAttrs->AddAttribute( USTR( "draw:style" ), USTR("ellipsoid") );
++ xAttrs->AddAttribute( USTR( "draw:cx" ), USTR("50%") );
++ xAttrs->AddAttribute( USTR( "draw:cy" ), USTR("50%") );
++ }
++
++ basegfx::B2DTuple rScale, rTranslate;
++ double rRotate, rShearX;
++ if( rState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
++ xAttrs->AddAttribute( USTR( "draw:angle" ),
++ rtl::OUString::valueOf(rRotate*1800.0/M_PI ) );
++ xAttrs->AddAttribute( USTR( "draw:start-color" ),
++ getOdfColor(
++ maGradientStopVector[
++ rState.maFillGradient.maStops[0]].maStopColor) );
++ xAttrs->AddAttribute( USTR( "draw:end-color" ),
++ getOdfColor(
++ maGradientStopVector[
++ rState.maFillGradient.maStops[1]].maStopColor) );
++ xAttrs->AddAttribute( USTR( "draw:border" ), USTR("0%") );
++ mxDocumentHandler->startElement( USTR("draw:gradient"),
++ xUnoAttrs );
++ mxDocumentHandler->endElement( USTR("draw:gradient") );
++
++ if( hasGradientOpacity(rState.maFillGradient) )
++ {
++ // need to write out opacity style as well
++ xAttrs->Clear();
++ xAttrs->AddAttribute( USTR( "draw:name" ), getStyleName("svgopacity", rState.maFillGradient.mnId) );
++ if( rState.maFillGradient.meType == Gradient::LINEAR )
++ {
++ xAttrs->AddAttribute( USTR( "draw:style" ), USTR("linear") );
++ }
++ else
++ {
++ xAttrs->AddAttribute( USTR( "draw:style" ), USTR("ellipsoid") );
++ xAttrs->AddAttribute( USTR( "draw:cx" ), USTR("50%") );
++ xAttrs->AddAttribute( USTR( "draw:cy" ), USTR("50%") );
++ }
++
++ // modulate gradient opacity with overall fill opacity
++ xAttrs->AddAttribute( USTR( "draw:end" ),
++ rtl::OUString::valueOf(
++ maGradientStopVector[
++ rState.maFillGradient.maStops[0]].maStopColor.a*
++ maCurrState.mnFillOpacity*100.0)+USTR("%" ) );
++ xAttrs->AddAttribute( USTR( "draw:start" ),
++ rtl::OUString::valueOf(
++ maGradientStopVector[
++ rState.maFillGradient.maStops[1]].maStopColor.a*
++ maCurrState.mnFillOpacity*100.0)+USTR("%" ) );
++ xAttrs->AddAttribute( USTR( "draw:border" ), USTR("0%") );
++ mxDocumentHandler->startElement( USTR("draw:opacity"),
++ xUnoAttrs );
++ mxDocumentHandler->endElement( USTR("draw:opacity") );
++ }
++ }
++
+ // serialize to automatic-style section
++ xAttrs->Clear();
+ xAttrs->AddAttribute( USTR( "style:name" ), getStyleName("svggraphicstyle", mnCurrStateId) );
+ xAttrs->AddAttribute( USTR( "style:family" ), USTR("graphic") );
+ mxDocumentHandler->startElement( USTR("style:style"),
+ xUnoAttrs );
+
+ xAttrs->Clear();
-+ if( rState.meFillType != NONE )
-+ {
-+ xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("solid"));
-+ xAttrs->AddAttribute( USTR( "draw:fill-color" ), getOdfColor(rState.maFillColor));
-+ if( maCurrState.mnFillOpacity != 1.0 )
-+ xAttrs->AddAttribute( USTR( "draw:fill-opacity" ), rtl::OUString::valueOf(maCurrState.mnFillOpacity));
-+ }
-+ else
-+ xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none"));
-+
-+ if( rState.meStrokeType != NONE )
-+ {
-+ xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("solid"));
-+ xAttrs->AddAttribute( USTR( "svg:stroke-color" ), getOdfColor(rState.maStrokeColor));
-+ }
-+ else
-+ xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("none"));
-+
-+ if( maCurrState.mnStrokeWidth != 0.0 )
-+ {
-+#if 1
-+ ::basegfx::B2DVector aVec(maCurrState.mnStrokeWidth,0);
-+ aVec *= maCurrState.maCTM;
-+ xAttrs->AddAttribute( USTR("svg:stroke-width"), rtl::OUString::valueOf( aVec.getLength()/1000.0 )+USTR("mm"));
-+#else
-+ xAttrs->AddAttribute( USTR("svg:stroke-width"), rtl::OUString::valueOf(maCurrState.mnStrokeWidth/100.0)+USTR("mm"));
-+#endif
-+ }
-+ if( maCurrState.meLineJoin == basegfx::tools::B2DLINEJOIN_MITER )
-+ xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("miter"));
-+ else if( maCurrState.meLineJoin == basegfx::tools::B2DLINEJOIN_ROUND )
-+ xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("round"));
-+ else if( maCurrState.meLineJoin == basegfx::tools::B2DLINEJOIN_BEVEL )
-+ xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("bevel"));
-+ if( maCurrState.mnStrokeOpacity != 1.0 )
-+ xAttrs->AddAttribute( USTR("svg:stroke-opacity"), rtl::OUString::valueOf(maCurrState.mnStrokeOpacity));
-+
-+ // beef up text box style with some auto-magic
++ // text or shape? if the former, no use in processing any
++ // graphic attributes except stroke color, ODF can do ~nothing
++ // with text shapes
+ if( nTagId == XML_TEXT )
+ {
-+ xAttrs->AddAttribute( USTR( "draw:auto-grow-height"), USTR("true"));
++ //xAttrs->AddAttribute( USTR( "draw:auto-grow-height"), USTR("true"));
+ xAttrs->AddAttribute( USTR( "draw:auto-grow-width"), USTR("true"));
+ xAttrs->AddAttribute( USTR( "draw:textarea-horizontal-align"), USTR("left"));
-+ xAttrs->AddAttribute( USTR( "textarea-vertical-align"), USTR("top"));
-+ xAttrs->AddAttribute( USTR( "fo:padding-top"), USTR("0cm"));
++ //xAttrs->AddAttribute( USTR( "draw:textarea-vertical-align"), USTR("top"));
++ xAttrs->AddAttribute( USTR( "fo:min-height"), USTR("0cm"));
++
+ xAttrs->AddAttribute( USTR( "fo:padding-top"), USTR("0cm"));
+ xAttrs->AddAttribute( USTR( "fo:padding-left"), USTR("0cm"));
+ xAttrs->AddAttribute( USTR( "fo:padding-right"), USTR("0cm"));
+ xAttrs->AddAttribute( USTR( "fo:padding-bottom"), USTR("0cm"));
++
++ // disable any background shape
++ xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("none"));
++ xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none"));
++ }
++ else
++ {
++ if( rState.meFillType != NONE )
++ {
++ if( rState.meFillType == GRADIENT )
++ {
++ xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("gradient"));
++ xAttrs->AddAttribute( USTR( "draw:fill-gradient-name" ),
++ getStyleName("svggradient", rState.maFillGradient.mnId) );
++ if( hasGradientOpacity(rState.maFillGradient) )
++ {
++ // needs transparency gradient as well
++ xAttrs->AddAttribute( USTR( "draw:opacity-name" ),
++ getStyleName("svgopacity", rState.maFillGradient.mnId) );
++ }
++ else if( maCurrState.mnFillOpacity != 1.0 )
++ xAttrs->AddAttribute( USTR( "draw:opacity" ),
++ rtl::OUString::valueOf(100.0*maCurrState.mnFillOpacity)+USTR("%") );
++ }
++ else
++ {
++ xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("solid"));
++ xAttrs->AddAttribute( USTR( "draw:fill-color" ), getOdfColor(rState.maFillColor));
++ if( maCurrState.mnFillOpacity != 1.0 )
++ xAttrs->AddAttribute( USTR( "draw:opacity" ),
++ rtl::OUString::valueOf(100.0*maCurrState.mnFillOpacity)+USTR("%") );
++ }
++ }
++ else
++ xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none"));
++
++ if( rState.meStrokeType != NONE )
++ {
++ xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("solid"));
++ xAttrs->AddAttribute( USTR( "svg:stroke-color" ), getOdfColor(rState.maStrokeColor));
++ }
++ else
++ xAttrs->AddAttribute( USTR( "draw:stroke" ), USTR("none"));
++
++ if( maCurrState.mnStrokeWidth != 0.0 )
++ {
++ ::basegfx::B2DVector aVec(maCurrState.mnStrokeWidth,0);
++ aVec *= maCurrState.maCTM;
++ xAttrs->AddAttribute( USTR("svg:stroke-width"), rtl::OUString::valueOf( aVec.getLength()/100.0 )+USTR("mm"));
++ }
++ if( maCurrState.meLineJoin == basegfx::tools::B2DLINEJOIN_MITER )
++ xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("miter"));
++ else if( maCurrState.meLineJoin == basegfx::tools::B2DLINEJOIN_ROUND )
++ xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("round"));
++ else if( maCurrState.meLineJoin == basegfx::tools::B2DLINEJOIN_BEVEL )
++ xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("bevel"));
++ if( maCurrState.mnStrokeOpacity != 1.0 )
++ xAttrs->AddAttribute( USTR("svg:stroke-opacity"),
++ rtl::OUString::valueOf(100.0*maCurrState.mnStrokeOpacity)+USTR("%"));
+ }
+
+ mxDocumentHandler->startElement( USTR("style:graphic-properties"),
@@ -2125,7 +2443,7 @@
+ return true; // newly written
+ }
+
-+ void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, sal_Int32 nTagId)
++ void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, const sal_Int32 nTagId)
+ {
+ sal_Int32 nEmulatedStyleId=0;
+ if( maCurrState.maDashArray.size() &&
@@ -2144,14 +2462,14 @@
+ aEmulatedStrokeState.meFillRule = EVEN_ODD;
+ aEmulatedStrokeState.meStrokeType = NONE;
+
-+ if( writeStyle(aEmulatedStrokeState,nTagId) )
++ if( writeStyle(aEmulatedStrokeState, nTagId) )
+ nEmulatedStyleId = mnCurrStateId;
+ else
+ nEmulatedStyleId = mrStates.find(aEmulatedStrokeState)->mnStyleId;
+ }
+
+ sal_Int32 nStyleId=0;
-+ if( writeStyle(maCurrState,nTagId) )
++ if( writeStyle(maCurrState, nTagId) )
+ nStyleId = mnCurrStateId;
+ else
+ nStyleId = mrStates.find(maCurrState)->mnStyleId;
@@ -2174,53 +2492,115 @@
+ maParentStates.pop_back();
+ }
+
-+ void parseGradientData( const sal_Int32 nTokenId,
-+ const rtl::OUString& sValue )
++ void parseLinearGradientData( Gradient& io_rCurrGradient,
++ const sal_Int32 nGradientNumber,
++ const sal_Int32 nTokenId,
++ const rtl::OUString& sValue )
+ {
+ switch(nTokenId)
+ {
++ case XML_GRADIENTTRANSFORM:
++ {
++ rtl::OString aValueUtf8( sValue.getStr(),
++ sValue.getLength(),
++ RTL_TEXTENCODING_UTF8 );
++ parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
++ break;
++ }
+ case XML_X1:
-+ mpCurrentGradient->mfX1 = convLength(sValue);
++ io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue);
+ break;
+ case XML_X2:
-+ mpCurrentGradient->mfX2 = convLength(sValue);
++ io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue);
+ break;
+ case XML_Y1:
-+ mpCurrentGradient->mfY1 = convLength(sValue);
++ io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue);
+ break;
+ case XML_Y2:
-+ mpCurrentGradient->mfY2 = convLength(sValue);
++ io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue);
+ break;
+ case XML_ID:
-+ mpCurrentGradient->msId = sValue;
++ maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
+ break;
+ case XML_GRADIENTUNITS:
+ if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
-+ mpCurrentGradient->mbBoundingBoxUnits = true;
++ io_rCurrGradient.mbBoundingBoxUnits = true;
+ else
-+ mpCurrentGradient->mbBoundingBoxUnits = false;
++ io_rCurrGradient.mbBoundingBoxUnits = false;
+ break;
-+ case XML_XLINK_HREF:
-+ if (sValue.copy(0,1).equalsAscii("#"))
-+ mpCurrentGradient->msLinkedTo = sValue.copy(1);
++ default:
++ break;
++ }
++ }
++
++ void parseRadialGradientData( Gradient& io_rCurrGradient,
++ const sal_Int32 nGradientNumber,
++ const sal_Int32 nTokenId,
++ const rtl::OUString& sValue )
++ {
++ switch(nTokenId)
++ {
++ case XML_GRADIENTTRANSFORM:
++ {
++ rtl::OString aValueUtf8( sValue.getStr(),
++ sValue.getLength(),
++ RTL_TEXTENCODING_UTF8 );
++ parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
++ break;
++ }
++ case XML_CX:
++ io_rCurrGradient.maCoords.radial.mfCX = convLength(sValue);
++ break;
++ case XML_CY:
++ io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue);
++ break;
++ case XML_FX:
++ io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue);
++ break;
++ case XML_FY:
++ io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue);
++ break;
++ case XML_R:
++ io_rCurrGradient.maCoords.radial.mfR = convLength(sValue);
++ break;
++ case XML_ID:
++ maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
++ break;
++ case XML_GRADIENTUNITS:
++ if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
++ io_rCurrGradient.mbBoundingBoxUnits = true;
+ else
-+ mpCurrentGradient->msLinkedTo = sValue;
++ io_rCurrGradient.mbBoundingBoxUnits = false;
+ break;
+ default:
+ break;
+ }
+ }
+
-+ void parseGradientStop( const sal_Int32 nTokenId,
++ void parseGradientStop( GradientStop& io_rGradientStop,
++ const sal_Int32 nStopNumber,
++ const sal_Int32 nTokenId,
+ const rtl::OUString& sValue )
+ {
+ switch(nTokenId)
+ {
++ case XML_HREF:
++ {
++ ElementRefMapType::iterator aFound=maStopIdMap.end();
++ if (sValue.copy(0,1).equalsAscii("#"))
++ aFound = maStopIdMap.find(sValue.copy(1));
++ else
++ aFound = maStopIdMap.find(sValue);;
++
++ if( aFound != maStopIdMap.end() )
++ io_rGradientStop = maGradientStopVector[aFound->second];
++ break;
++ }
+ case XML_ID:
-+ mpCurrentGradientStop->msId = sValue;
++ maStopIdMap.insert(std::make_pair(sValue,nStopNumber));
+ break;
+ case XML_OFFSET:
-+ mpCurrentGradientStop->mnStopPosition = sValue.toDouble();
++ io_rGradientStop.mnStopPosition = sValue.toDouble();
+ break;
+ case XML_STYLE:
+ parseStyle( sValue );
@@ -2274,6 +2654,12 @@
+ maCurrState.meFillRule = maParentStates.back().meFillRule;
+ break;
+ }
++ case XML_FILL_OPACITY:
++ if( aValueUtf8 == "inherit" )
++ maCurrState.mnFillOpacity = maParentStates.back().mnFillOpacity;
++ else
++ maCurrState.mnFillOpacity = aValueUtf8.toDouble();
++ break;
+ case XML_STROKE_WIDTH:
+ {
+ if( aValueUtf8 == "inherit" )
@@ -2333,10 +2719,17 @@
+ maCurrState.maDashArray);
+ break;
+ }
++ case XML_STROKE_OPACITY:
++ if( aValueUtf8 == "inherit" )
++ maCurrState.mnStrokeOpacity = maParentStates.back().mnStrokeOpacity;
++ else
++ maCurrState.mnStrokeOpacity = aValueUtf8.toDouble();
++ break;
+ case XML_FILL:
+ {
+ const State& rParent( maParentStates.back() );
-+ parsePaint( aValueUtf8.getStr(),
++ parsePaint( sValue,
++ aValueUtf8.getStr(),
+ maCurrState.meFillType,
+ maCurrState.maFillColor,
+ maCurrState.maFillGradient,
@@ -2348,7 +2741,8 @@
+ case XML_STROKE:
+ {
+ const State& rParent( maParentStates.back() );
-+ parsePaint( aValueUtf8.getStr(),
++ parsePaint( sValue,
++ aValueUtf8.getStr(),
+ maCurrState.meStrokeType,
+ maCurrState.maStrokeColor,
+ maCurrState.maStrokeGradient,
@@ -2383,10 +2777,20 @@
+ maCurrState.meFontVariant=VARIANT_SMALLCAPS; // TODO: sValue.toDouble();
+ break;
+ case XML_STOP_COLOR:
-+ parseColor( aValueUtf8, mpCurrentGradientStop->maStopColor );
++ if( maGradientVector.empty() ||
++ maGradientVector.back().maStops.empty() )
++ break;
++ parseColor( aValueUtf8,
++ maGradientStopVector[
++ maGradientVector.back().maStops.back()].maStopColor );
+ break;
+ case XML_STOP_OPACITY:
-+ parseOpacity( aValueUtf8, mpCurrentGradientStop->maStopColor );
++ if( maGradientVector.empty() ||
++ maGradientVector.back().maStops.empty() )
++ break;
++ parseOpacity( aValueUtf8,
++ maGradientStopVector[
++ maGradientVector.back().maStops.back()].maStopColor );
+ break;
+ default:
+ OSL_TRACE("unhandled token %s", getTokenName(nTokenId));
@@ -2420,7 +2824,8 @@
+ while( nIndex != -1 );
+ }
+
-+ void parsePaint( const char* sValue,
++ void parsePaint( const rtl::OUString& rValue,
++ const char* sValue,
+ PaintType& rType,
+ ARGBColor& rColor,
+ Gradient& rGradient,
@@ -2441,8 +2846,19 @@
+ rColor = rInheritColor;
+ rGradient = rInheritGradient;
+ }
-+ else if( strncmp(sValue,"url",3) == 0)
++ else if( strncmp(sValue,"url(#",5) == 0 )
+ {
++ // assuming gradient. assumption does not hold generally
++ if( rValue.getLength() > 5 )
++ {
++ ElementRefMapType::iterator aRes;
++ if( (aRes=maGradientIdMap.find(rValue.copy(5,
++ rValue.getLength()-6))) != maGradientIdMap.end() )
++ {
++ rGradient = maGradientVector[aRes->second];
++ rType = GRADIENT;
++ }
++ }
+ }
+ else
+ {
@@ -2451,15 +2867,16 @@
+ }
+ }
+
-+ sal_Int32 mnCurrStateId;
-+ State maCurrState;
-+ std::vector<State> maParentStates;
-+ StatePool& mrStates;
-+ StateMap& mrStateMap;
-+ uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
-+ Gradient* mpCurrentGradient;
-+ GradientStop* mpCurrentGradientStop;
-+ std::vector< Gradient > mxGradientVector;
++ sal_Int32 mnCurrStateId;
++ State maCurrState;
++ std::vector<State> maParentStates;
++ StatePool& mrStates;
++ StateMap& mrStateMap;
++ uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
++ std::vector< Gradient > maGradientVector;
++ std::vector< GradientStop > maGradientStopVector;
++ ElementRefMapType maGradientIdMap;
++ ElementRefMapType maStopIdMap;
+};
+
+/// Annotate svg styles with unique references to state pool
@@ -2807,15 +3224,35 @@
+
+ // actually export text
+ xAttrs->Clear();
++
++ // extract basic transformations out of CTM
++ basegfx::B2DTuple aScale, aTranslate;
++ double fRotate, fShearX;
++ ::rtl::OUString sTransformValue;
++ if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
++ {
++ rtl::OUString sTransform;
++ x += aTranslate.getX();
++ y += aTranslate.getY();
++
++ sTransform +=
++ USTR("scale(") +
++ rtl::OUString::valueOf(aScale.getX()) +
++ USTR(", ") +
++ rtl::OUString::valueOf(aScale.getX()) +
++ USTR(")");
++
++ if( fRotate )
++ sTransform += USTR(" rotate(") + rtl::OUString::valueOf(fRotate*180.0/M_PI) + USTR(")");
++
++ if( fShearX )
++ sTransform += USTR(" skewX(") + rtl::OUString::valueOf(fShearX*180.0/M_PI) + USTR(")");
++ }
++
+ xAttrs->AddAttribute( USTR( "svg:x" ), rtl::OUString::valueOf(x/100.0)+USTR("mm"));
+ xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(y/100.0)+USTR("mm"));
-+#if 0
-+ xAttrs->AddAttribute( USTR( "svg:width" ), rtl::OUString::valueOf(std::max(10.0,width/100.0))+USTR("mm"));
-+ xAttrs->AddAttribute( USTR( "svg:height" ), rtl::OUString::valueOf(std::max(10.0,height/100.0))+USTR("mm"));
-+#else
-+ xAttrs->AddAttribute( USTR( "svg:width" ), USTR("100mm"));
-+ xAttrs->AddAttribute( USTR( "svg:height" ), USTR("100mm"));
-+#endif
++ //xAttrs->AddAttribute( USTR( "svg:width" ), rtl::OUString::valueOf(width/100.0)+USTR("mm"));
++ //xAttrs->AddAttribute( USTR( "svg:height" ), rtl::OUString::valueOf(height/100.0)+USTR("mm"));
+ xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("svggraphicstyle")+sStyleId );
+
+ mxDocumentHandler->startElement(USTR("draw:frame"),xUnoAttrs);
@@ -3351,6 +3788,653 @@
+} // namespace svgi
+
+#endif
+--- filter/source/svg/test/makefile.mk 1970-01-01 01:00:00.000000000 +0100
++++ filter/source/svg/test/makefile.mk 2008-05-23 11:23:50.000000000 +0200
+@@ -0,0 +1,123 @@
++#*************************************************************************
++#
++# OpenOffice.org - a multi-platform office productivity suite
++#
++# Author:
++# Fridrich Strba <fridrich strba bluewin ch>
++# Thorsten Behrens <tbehrens novell com>
++#
++# Copyright (C) 2008, Novell Inc.
++# Parts copyright 2005 by Sun Microsystems, Inc.
++#
++# The Contents of this file are made available subject to
++# the terms of GNU Lesser General Public License Version 2.1.
++#
++#*************************************************************************
++
++PRJ=..$/..$/..
++PRJNAME=filter
++TARGET=tests
++TARGETTYPE=CUI
++ENABLE_EXCEPTIONS=TRUE
++
++# --- Settings -----------------------------------------------------
++
++.INCLUDE: settings.mk
++
++# --- unit tests ---------------------------------------------------
++
++SHL1OBJS= \
++ $(SLO)$/parsertest.obj
++
++SHL1TARGET= tests
++SHL1LIBS= $(SLB)$/svgfilter.lib
++SHL1STDLIBS= \
++ $(BASEGFXLIB) \
++ $(SVXLIB) \
++ $(XMLOFFLIB) \
++ $(BASEGFXLIB) \
++ $(VCLLIB) \
++ $(UNOTOOLSLIB) \
++ $(TOOLSLIB) \
++ $(COMPHELPERLIB) \
++ $(CPPUHELPERLIB) \
++ $(CPPULIB) \
++ $(SALLIB) \
++ $(LIBXML) \
++ $(CPPUNITLIB)
++
++# --- svg2xml binary ------------------------------------------------------
++
++TARGET2=svg2odf
++
++APP1TARGET=$(TARGET2)
++APP1LIBSALCPPRT=
++APP1OBJS= \
++ $(SLO)$/odfserializer.obj \
++ $(SLO)$/svg2odf.obj
++
++APP1LIBS=\
++ $(SLB)$/svgfilter.lib
++
++APP1STDLIBS=\
++ $(BASEGFXLIB) \
++ $(SVXLIB) \
++ $(XMLOFFLIB) \
++ $(BASEGFXLIB) \
++ $(VCLLIB) \
++ $(UNOTOOLSLIB) \
++ $(TOOLSLIB) \
++ $(COMPHELPERLIB) \
++ $(CPPUHELPERLIB) \
++ $(CPPULIB) \
++ $(SALLIB) \
++ $(LIBXML)
++
++# --- Targets ------------------------------------------------------
++
++.INCLUDE : target.mk
++.INCLUDE : _cppunit.mk
++
++# --- Special ------------------------------------------------------
++
++TESTFILES=\
++ anarchist.svg \
++ andorra.svg \
++ butterfly.svg \
++ daisies.svg \
++ ellipticarcs.svg \
++ firewall_denco_01.svg \
++ mediatrice.svg \
++ mouse_the_structorr.svg \
++ network_could_nicolas_cl.svg \
++ otto_01.svg \
++ roundingerrors.svg \
++ test.svg \
++ tiger.svg
++
++$(MISC)$/%_svgi_unittest_succeeded : $(BIN)$/svg2odf
++ rm -f $(MISC)$/$(@:s/_succeeded/.xml/:f)
++ $(BIN)$/svg2odf $(@:s/_svgi_unittest_succeeded/.svg/:f) $(MISC)$/$(@:s/_succeeded/.xml/:f) $(BIN)$/svgi_unittest_test.ini
++ $(TOUCH) $@
++
++.IF "$(GUI)" == "WNT"
++SAXPARSERLIB=$(SOLARBINDIR)$/sax.uno$(DLLPOST)
++UNOXMLLIB=$(SOLARBINDIR)$/$(DLLPRE)unoxml$(OFFICEUPD)$(DLLPOSTFIX)$(DLLPOST)
++.ELSE
++SAXPARSERLIB=$(SOLARLIBDIR)$/sax.uno$(DLLPOST)
++UNOXMLLIB=$(SOLARLIBDIR)$/$(DLLPRE)unoxml$(OFFICEUPD)$(DLLPOSTFIX)$(DLLPOST)
++.ENDIF
++
++$(BIN)$/unittestservices.rdb : makefile.mk $(SAXPARSERLIB) $(UNOXMLLIB)
++ rm -f $@
++ $(REGCOMP) -register -r $@ -c $(SAXPARSERLIB)
++ $(REGCOMP) -register -r $@ -c $(UNOXMLLIB)
++
++$(BIN)$/svgi_unittest_test.ini : makefile.mk
++ rm -f $@
++ @echo UNO_SERVICES=$(BIN)$/unittestservices.rdb > $@
++ @echo UNO_TYPES=$(UNOUCRRDB:s/\/\\/) >> $@
++
++#ALLTAR : $(BIN)$/svgi_unittest_test.ini \
++# $(BIN)$/unittestservices.rdb \
++# $(foreach,i,$(TESTFILES:s/.svg/_svgi_unittest_succeeded/:f) $(MISC)$/$i)
+diff -urN /tmp/svgfilter/filter/source/svg/test/odfserializer.cxx /builds/oobuild/ooo-build-2.4/build/current/filter/source/svg/test/odfserializer.cxx
+--- filter/source/svg/test/odfserializer.cxx 1970-01-01 01:00:00.000000000 +0100
++++ filter/source/svg/test/odfserializer.cxx 2008-05-23 10:23:53.000000000 +0200
+@@ -0,0 +1,140 @@
++/*************************************************************************
++ *
++ * OpenOffice.org - a multi-platform office productivity suite
++ *
++ * Author:
++ * Fridrich Strba <fridrich strba bluewin ch>
++ * Thorsten Behrens <tbehrens novell com>
++ *
++ * Copyright (C) 2008, Novell Inc.
++ * Parts copyright 2005 by Sun Microsystems, Inc.
++ *
++ * The Contents of this file are made available subject to
++ * the terms of GNU Lesser General Public License Version 2.1.
++ *
++ ************************************************************************/
++
++// MARKER(update_precomp.py): autogen include statement, do not remove
++#include "precompiled_filter.hxx"
++
++#include "odfserializer.hxx"
++#include <osl/diagnose.h>
++#include <rtl/ustrbuf.hxx>
++#include <cppuhelper/compbase1.hxx>
++#include <cppuhelper/basemutex.hxx>
++#include <com/sun/star/uno/Sequence.hxx>
++#include <boost/noncopyable.hpp>
++
++using namespace ::com::sun::star;
++
++namespace svgi
++{
++
++typedef ::cppu::WeakComponentImplHelper1<
++ com::sun::star::xml::sax::XDocumentHandler> ODFSerializerBase;
++
++class ODFSerializer : private cppu::BaseMutex,
++ public ODFSerializerBase,
++ boost::noncopyable
++{
++public:
++ explicit ODFSerializer(const uno::Reference<io::XOutputStream>& xOut) :
++ ODFSerializerBase(m_aMutex),
++ m_xOutStream(xOut),
++ m_aLineFeed(1),
++ m_aBuf()
++ {
++ m_aLineFeed[0] = '\n';
++ }
++
++ virtual void SAL_CALL startDocument( ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL endDocument( ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL startElement( const ::rtl::OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL endElement( const ::rtl::OUString& aName ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL characters( const ::rtl::OUString& aChars ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL ignorableWhitespace( const ::rtl::OUString& aWhitespaces ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL processingInstruction( const ::rtl::OUString& aTarget, const ::rtl::OUString& aData ) throw (xml::sax::SAXException, uno::RuntimeException);
++ virtual void SAL_CALL setDocumentLocator( const uno::Reference< xml::sax::XLocator >& xLocator ) throw (xml::sax::SAXException, uno::RuntimeException);
++
++private:
++ uno::Reference<io::XOutputStream> m_xOutStream;
++ uno::Sequence<sal_Int8> m_aLineFeed;
++ uno::Sequence<sal_Int8> m_aBuf;
++};
++
++void SAL_CALL ODFSerializer::startDocument( ) throw (xml::sax::SAXException, uno::RuntimeException)
++{
++ OSL_PRECOND(m_xOutStream.is(), "ODFSerializer(): invalid output stream");
++
++ rtl::OUStringBuffer aElement;
++ aElement.appendAscii("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
++ characters(aElement.makeStringAndClear());
++}
++
++void SAL_CALL ODFSerializer::endDocument() throw (xml::sax::SAXException, uno::RuntimeException)
++{}
++
++void SAL_CALL ODFSerializer::startElement( const ::rtl::OUString& aName,
++ const uno::Reference< xml::sax::XAttributeList >& xAttribs ) throw (xml::sax::SAXException, uno::RuntimeException)
++{
++ rtl::OUStringBuffer aElement;
++ aElement.appendAscii("<");
++ aElement.append(aName);
++ aElement.appendAscii(" ");
++
++ const sal_Int16 nLen=xAttribs->getLength();
++ for( sal_Int16 i=0; i<nLen; ++i )
++ {
++ rtl::OUStringBuffer aAttribute;
++ aElement.append(xAttribs->getNameByIndex(i));
++ aElement.appendAscii("=\"");
++ aElement.append(xAttribs->getValueByIndex(i));
++ aElement.appendAscii("\" ");
++ }
++
++ aElement.appendAscii(">");
++ characters(aElement.makeStringAndClear());
++}
++
++void SAL_CALL ODFSerializer::endElement( const ::rtl::OUString& aName ) throw (xml::sax::SAXException, uno::RuntimeException)
++{
++ rtl::OUStringBuffer aElement;
++ aElement.appendAscii("</");
++ aElement.append(aName);
++ aElement.appendAscii(">");
++ characters(aElement.makeStringAndClear());
++}
++
++void SAL_CALL ODFSerializer::characters( const ::rtl::OUString& aChars ) throw (xml::sax::SAXException, uno::RuntimeException)
++{
++ const rtl::OString aStr = rtl::OUStringToOString(aChars,
++ RTL_TEXTENCODING_UTF8);
++ const sal_Int32 nLen( aStr.getLength() );
++ m_aBuf.realloc( nLen );
++ const sal_Char* pStr = aStr.getStr();
++ std::copy(pStr,pStr+nLen,m_aBuf.getArray());
++
++ m_xOutStream->writeBytes(m_aBuf);
++ // TODO(F1): Make pretty printing configurable
++ m_xOutStream->writeBytes(m_aLineFeed);
++}
++
++void SAL_CALL ODFSerializer::ignorableWhitespace( const ::rtl::OUString& aWhitespaces ) throw (xml::sax::SAXException, uno::RuntimeException)
++{
++ // TODO(F1): Make pretty printing configurable
++ characters(aWhitespaces);
++}
++
++void SAL_CALL ODFSerializer::processingInstruction( const ::rtl::OUString&,
++ const ::rtl::OUString& ) throw (xml::sax::SAXException, uno::RuntimeException)
++{}
++
++void SAL_CALL ODFSerializer::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& ) throw (xml::sax::SAXException, uno::RuntimeException)
++{}
++
++uno::Reference< xml::sax::XDocumentHandler> createSerializer(const uno::Reference<io::XOutputStream>& xOut )
++{
++ return uno::Reference<xml::sax::XDocumentHandler>(new ODFSerializer(xOut));
++}
++
++}
+diff -urN /tmp/svgfilter/filter/source/svg/test/odfserializer.hxx /builds/oobuild/ooo-build-2.4/build/current/filter/source/svg/test/odfserializer.hxx
+--- filter/source/svg/test/odfserializer.hxx 1970-01-01 01:00:00.000000000 +0100
++++ filter/source/svg/test/odfserializer.hxx 2008-05-23 10:23:53.000000000 +0200
+@@ -0,0 +1,31 @@
++/*************************************************************************
++ *
++ * OpenOffice.org - a multi-platform office productivity suite
++ *
++ * Author:
++ * Fridrich Strba <fridrich strba bluewin ch>
++ * Thorsten Behrens <tbehrens novell com>
++ *
++ * Copyright (C) 2008, Novell Inc.
++ * Parts copyright 2005 by Sun Microsystems, Inc.
++ *
++ * The Contents of this file are made available subject to
++ * the terms of GNU Lesser General Public License Version 2.1.
++ *
++ ************************************************************************/
++
++#ifndef INCLUDED_SVG_ODFSERIALIZER_HXX
++#define INCLUDED_SVG_ODFSERIALIZER_HXX
++
++#include <com/sun/star/uno/Reference.hxx>
++#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
++#include <com/sun/star/io/XOutputStream.hpp>
++
++namespace svgi
++{
++ /// Creates a XDocumentHandler that serializes directly to an XOutputStream
++ ::com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler>
++ createSerializer(const ::com::sun::star::uno::Reference<com::sun::star::io::XOutputStream>& );
++}
++
++#endif // _COM_SUN_STAR_XML_SAX_XDOCUMENTHANDLER_HDL_
+diff -urN /tmp/svgfilter/filter/source/svg/test/parsertest.cxx /builds/oobuild/ooo-build-2.4/build/current/filter/source/svg/test/parsertest.cxx
+--- filter/source/svg/test/parsertest.cxx 1970-01-01 01:00:00.000000000 +0100
++++ filter/source/svg/test/parsertest.cxx 2008-05-23 15:52:49.000000000 +0200
+@@ -0,0 +1,210 @@
++/*************************************************************************
++ *
++ * OpenOffice.org - a multi-platform office productivity suite
++ *
++ * Author:
++ * Fridrich Strba <fridrich strba bluewin ch>
++ * Thorsten Behrens <tbehrens novell com>
++ *
++ * Copyright (C) 2008, Novell Inc.
++ *
++ * The Contents of this file are made available subject to
++ * the terms of GNU Lesser General Public License Version 2.1.
++ *
++ ************************************************************************/
++
++// MARKER(update_precomp.py): autogen include statement, do not remove
++#include "precompiled_filter.hxx"
++
++#include <cppunit/simpleheader.hxx>
++
++#include "../gfxtypes.hxx"
++#include "../parserfragments.hxx"
++
++using namespace svgi;
++
++class TestParser : public CppUnit::TestFixture
++{
++public:
++ void setUp()
++ {}
++
++ void tearDown()
++ {}
++
++ void testParseColor()
++ {
++ ARGBColor aTmp;
++
++ const char* sIn="#102030 ";
++ ARGBColor aOut(16, 32, 48);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming color #112233",
++ parseColor( sIn, aTmp ) );
++ OSL_TRACE("color is: a:%f r:%f g:%f b:%f", aTmp.a, aTmp.r, aTmp.g, aTmp.b);
++ CPPUNIT_ASSERT_MESSAGE( "Parsing color #112233",
++ aOut==aTmp );
++
++ sIn=" #321";
++ aOut=ARGBColor(51, 34, 17);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming color #321",
++ parseColor( sIn, aTmp ) );
++ OSL_TRACE("color is: a:%f r:%f g:%f b:%f", aTmp.a, aTmp.r, aTmp.g, aTmp.b);
++ CPPUNIT_ASSERT_MESSAGE( "Parsing color #321",
++ aOut==aTmp );
++
++ sIn="rgb(100,200,\t 50)";
++ aOut=ARGBColor(100, 200, 50);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming color rgb(100,200,50)",
++ parseColor( sIn, aTmp ) );
++ OSL_TRACE("color is: a:%f r:%f g:%f b:%f", aTmp.a, aTmp.r, aTmp.g, aTmp.b);
++ CPPUNIT_ASSERT_MESSAGE( "Parsing color rgb(100,200,50)",
++ aOut==aTmp );
++
++ sIn="rgb(0.1, \t0.2,0.9)";
++ aOut=ARGBColor(0.1, 0.2, 0.9);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming color rgb(0.1,0.2,0.9)",
++ parseColor( sIn, aTmp ) );
++ OSL_TRACE("color is: a:%f r:%f g:%f b:%f", aTmp.a, aTmp.r, aTmp.g, aTmp.b);
++ CPPUNIT_ASSERT_MESSAGE( "Parsing color rgb(0.1,0.2,0.9)",
++ aOut==aTmp );
++
++ sIn=" burlywood ";
++ aOut=ARGBColor(222,184,135);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming color burlywood",
++ parseColor( sIn, aTmp ) );
++ OSL_TRACE("color is: a:%f r:%f g:%f b:%f", aTmp.a, aTmp.r, aTmp.g, aTmp.b);
++ CPPUNIT_ASSERT_MESSAGE( "Parsing color burlywood",
++ aOut==aTmp );
++ }
++
++ void testParseOpacity()
++ {
++ ARGBColor aTmp;
++
++ const char* sIn=" 0.123 ";
++ ARGBColor aOut(0.123, 0.0, 0.0, 0.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming opacity 0.123",
++ parseOpacity( sIn, aTmp ) );
++ OSL_TRACE("color is: a:%f r:%f g:%f b:%f", aTmp.a, aTmp.r, aTmp.g, aTmp.b);
++ CPPUNIT_ASSERT_MESSAGE( "Parsing opacity 0.123",
++ aOut==aTmp );
++ }
++
++ void testParseTransform()
++ {
++ basegfx::B2DHomMatrix aOut;
++
++ const char* sIn=" none ";
++ basegfx::B2DHomMatrix aTmp;
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation none",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation none",
++ aOut==aTmp );
++
++ sIn=" scale( 10 ) ";
++ aOut.identity();
++ aOut.scale(10.0,10.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation scale(10)",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation scale(10)",
++ aOut==aTmp );
++
++ sIn=" scale( 10 20.12 ) ";
++ aOut.identity();
++ aOut.scale(10.0,20.12);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation scale(10 20.12)",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation scale(10 20.12)",
++ aOut==aTmp );
++
++ sIn="matrix( 1,2 3,4,5 6 )";
++ aOut.identity();
++ aOut.set(0,0,1.0); aOut.set(1,0,2.0); aOut.set(0,1,3.0); aOut.set(1,1,4.0); aOut.set(0,2,5.0); aOut.set(1,2,6.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation matrix(1,2,3,4,5,6)",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation matrix(1,2,3,4,5,6)",
++ aOut==aTmp );
++
++ sIn="matrix( 1 0 0 1 -10 -10 ) translate(10) scale(10), rotate(90)";
++ aOut.identity();
++ aOut.set(0,0,0.0); aOut.set(1,0,10.0); aOut.set(0,1,-10.0); aOut.set(1,1,0.0); aOut.set(0,2,0.0); aOut.set(1,2,0.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation matrix(1,2,3,4,5,6)",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation matrix(1,2,3,4,5,6)",
++ aOut==aTmp );
++
++ sIn="skewX(45)";
++ aOut.identity();
++ aOut.set(0,0,1.0); aOut.set(1,0,1.0); aOut.set(0,1,0.0); aOut.set(1,1,1.0); aOut.set(0,2,0.0); aOut.set(1,2,0.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation skewX(45)",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation skewX(45)",
++ aOut==aTmp );
++
++ sIn="skewY(45)";
++ aOut.identity();
++ aOut.set(0,0,1.0); aOut.set(1,0,0.0); aOut.set(0,1,1.0); aOut.set(1,1,1.0); aOut.set(0,2,0.0); aOut.set(1,2,0.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming transformation skewY(45)",
++ parseTransform( sIn, aTmp ) );
++ OSL_TRACE("transformation is: m00:%f m01:%f m02:%f m10:%f m11:%f m12:%f",
++ aTmp.get(0,0), aTmp.get(0,1), aTmp.get(0,2), aTmp.get(1,0), aTmp.get(1,1), aTmp.get(1,2) );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing transformation skewY(45)",
++ aOut==aTmp );
++ }
++
++ void testParseViewBox()
++ {
++ basegfx::B2DRange aTmp;
++
++ const char* sIn=" 10 20, 30.5,5 ";
++ basegfx::B2DRange aOut(10,20,40.5,25);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming 10,20,30.5,5",
++ parseViewBox( sIn, aTmp ) );
++ OSL_TRACE("viewbox is: x1:%f y1:%f x2:%f y2:%f", aTmp.getMinX(), aTmp.getMinY(), aTmp.getMaxX(), aTmp.getMaxY());
++ CPPUNIT_ASSERT_MESSAGE( "Parsing 10,20,30.5,5",
++ aOut==aTmp );
++ }
++
++ void testParseDashArray()
++ {
++ std::vector<double> aTmp;
++
++ const char* sIn=" 10,20, -10.00 ";
++ std::vector<double> aOut; aOut.push_back(10.0); aOut.push_back(20.0); aOut.push_back(-10.0);
++ CPPUNIT_ASSERT_MESSAGE( "Consuming 10,20,-10.00",
++ parseDashArray( sIn, aTmp ) );
++ OSL_TRACE("dash array is: len %d, %f %f %f", aTmp.size(), aTmp[0], aTmp[1], aTmp[2] );
++ CPPUNIT_ASSERT_MESSAGE( "Parsing 10,20,-10.00",
++ aOut==aTmp );
++ }
++
++ CPPUNIT_TEST_SUITE(TestParser);
++ CPPUNIT_TEST(testParseColor);
++ CPPUNIT_TEST(testParseOpacity);
++ CPPUNIT_TEST(testParseTransform);
++ CPPUNIT_TEST(testParseViewBox);
++ CPPUNIT_TEST(testParseDashArray);
++ // TODO: CPPUNIT_TEST(testParseXlinkHref);
++ CPPUNIT_TEST_SUITE_END();
++};
++
++// -----------------------------------------------------------------------------
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TestParser, "test svg parser fragments");
++
++// this macro creates an empty function, which will called by the RegisterAllFunctions()
++// to let the user the possibility to also register some functions by hand.
++NOADDITIONAL;
++
+diff -urN /tmp/svgfilter/filter/source/svg/test/svg2odf.cxx /builds/oobuild/ooo-build-2.4/build/current/filter/source/svg/test/svg2odf.cxx
+--- filter/source/svg/test/svg2odf.cxx 1970-01-01 01:00:00.000000000 +0100
++++ filter/source/svg/test/svg2odf.cxx 2008-05-23 11:23:07.000000000 +0200
+@@ -0,0 +1,124 @@
++/*************************************************************************
++ *
++ * OpenOffice.org - a multi-platform office productivity suite
++ *
++ * Author:
++ * Fridrich Strba <fridrich strba bluewin ch>
++ * Thorsten Behrens <tbehrens novell com>
++ *
++ * Copyright (C) 2008, Novell Inc.
++ * Parts copyright 2005 by Sun Microsystems, Inc.
++ *
++ * The Contents of this file are made available subject to
++ * the terms of GNU Lesser General Public License Version 2.1.
++ *
++ ************************************************************************/
++
++// MARKER(update_precomp.py): autogen include statement, do not remove
++#include "precompiled_filter.hxx"
++
++#include "../svgreader.hxx"
++#include "odfserializer.hxx"
++
++#include <sal/main.h>
++#include <osl/file.hxx>
++#include <osl/process.h>
++#include <rtl/bootstrap.hxx>
++
++#include <cppuhelper/implbase1.hxx>
++#include <cppuhelper/bootstrap.hxx>
++#include <cppuhelper/servicefactory.hxx>
++#include <comphelper/processfactory.hxx>
++#include <comphelper/oslfile2streamwrap.hxx>
++
++using namespace ::com::sun::star;
++
++namespace
++{
++ class OutputWrap : public cppu::WeakImplHelper1<
++ io::XOutputStream>
++ {
++ osl::File maFile;
++
++ public:
++
++ explicit OutputWrap( const rtl::OUString& rURL ) : maFile(rURL)
++ {
++ maFile.open(osl_File_OpenFlag_Create|OpenFlag_Write);
++ }
++
++ virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< ::sal_Int8 >& aData ) throw (com::sun::star::io::NotConnectedException,com::sun::star::io::BufferSizeExceededException, com::sun::star::io::IOException, com::sun::star::uno::RuntimeException)
++
++ {
++ sal_uInt64 nBytesWritten(0);
++ maFile.write(aData.getConstArray(),aData.getLength(),nBytesWritten);
++ }
++
++ virtual void SAL_CALL flush() throw (com::sun::star::io::NotConnectedException, com::sun::star::io::BufferSizeExceededException, com::sun::star::io::IOException, com::sun::star::uno::RuntimeException)
++ {
++ }
++
++ virtual void SAL_CALL closeOutput() throw (com::sun::star::io::NotConnectedException, com::sun::star::io::BufferSizeExceededException, com::sun::star::io::IOException, com::sun::star::uno::RuntimeException)
++ {
++ maFile.close();
++ }
++ };
++}
++
++SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
++{
++ if( argc != 4 )
++ {
++ OSL_TRACE( "Invocation: svg2odf <base_url> <dst_url> <ini_file>. Exiting" );
++ return 1;
++ }
++
++ ::rtl::OUString aBaseURL, aTmpURL, aSrcURL, aDstURL, aIniUrl;
++
++ osl_getProcessWorkingDir(&aBaseURL.pData);
++ osl_getFileURLFromSystemPath( rtl::OUString::createFromAscii(argv[1]).pData,
++ &aTmpURL.pData );
++ osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aSrcURL.pData);
++
++ osl_getFileURLFromSystemPath( rtl::OUString::createFromAscii(argv[2]).pData,
++ &aTmpURL.pData );
++ osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aDstURL.pData);
++
++ osl_getFileURLFromSystemPath( rtl::OUString::createFromAscii(argv[3]).pData,
++ &aTmpURL.pData );
++ osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aIniUrl.pData);
++
++ // bootstrap UNO
++ uno::Reference< lang::XMultiServiceFactory > xFactory;
++ uno::Reference< uno::XComponentContext > xCtx;
++ try
++ {
++ xCtx = ::cppu::defaultBootstrap_InitialComponentContext(aIniUrl);
++ xFactory = uno::Reference< lang::XMultiServiceFactory >(xCtx->getServiceManager(),
++ uno::UNO_QUERY);
++ if( xFactory.is() )
++ ::comphelper::setProcessServiceFactory( xFactory );
++ }
++ catch( uno::Exception& )
++ {
++ }
++
++ if( !xFactory.is() )
++ {
++ OSL_TRACE( "Could not bootstrap UNO, installation must be in disorder. Exiting." );
++ return 1;
++ }
++
++ osl::File aInputFile(aSrcURL);
++ if( osl::FileBase::E_None!=aInputFile.open(OpenFlag_Read) )
++ {
++ OSL_TRACE( "Cannot open input file" );
++ return 1;
++ }
++
++ svgi::SVGReader aReader(xFactory,
++ uno::Reference<io::XInputStream>(
++ new comphelper::OSLInputStreamWrapper(aInputFile)),
++ svgi::createSerializer(new OutputWrap(aDstURL)));
++ return aReader.parseAndConvert() ? 0 : 1;
++}
--- filter/source/svg/tokenmap.cxx 1970-01-01 01:00:00.000000000 +0100
+++ filter/source/svg/tokenmap.cxx 2008-04-11 00:09:55.000000000 +0200
@@ -0,0 +1,62 @@
@@ -3453,7 +4537,7 @@
+#endif
--- filter/source/svg/tokens.txt 1970-01-01 01:00:00.000000000 +0100
+++ filter/source/svg/tokens.txt 2008-04-11 00:09:55.000000000 +0200
-@@ -0,0 +1,405 @@
+@@ -0,0 +1,403 @@
+#######################################
+#
+# elements (SVG Tiny 1.2)
@@ -3578,7 +4662,6 @@
+dur
+editable
+end
-+ev:event
+event
+externalResourcesRequired
+fill
@@ -3590,9 +4673,12 @@
+font-variant
+font-weight
+from
++fx
++fy
+g1
+g2
+glyph-name
++gradientTransform
+gradientUnits
+handler
+hanging
@@ -3685,17 +4771,13 @@
+x-height
+x1
+x2
-+xlink:actuate
-+xlink:arcrole
-+xlink:href
-+xlink:role
-+xlink:show
-+xlink:title
-+xlink:type
-+xml:base
-+xml:id
-+xml:lang
-+xml:space
++actuate
++arcrole
++href
++role
++show
++base
++space
+y
+y1
+y2
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]