diff options
author | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2010-09-14 09:36:14 +0200 |
---|---|---|
committer | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2010-09-14 09:36:14 +0200 |
commit | 90f5ce36231551e226d4b3e2fefaa8493af692ac (patch) | |
tree | f8df50349969a0b2b6d9f1d526fce48661507fd7 /filter | |
parent | 24dd42f316e5cb16ba726c05ba486d53e033052d (diff) |
svg-import-filter.diff: SVG Import Filter implementation in filter module
Diffstat (limited to 'filter')
26 files changed, 4695 insertions, 255 deletions
diff --git a/filter/source/config/fragments/fcfg_drawgraphics.mk b/filter/source/config/fragments/fcfg_drawgraphics.mk index 7038e27e8ae4..41a46254b871 100644 --- a/filter/source/config/fragments/fcfg_drawgraphics.mk +++ b/filter/source/config/fragments/fcfg_drawgraphics.mk @@ -54,6 +54,7 @@ F4_DRAWGRAPHICS = \ SGF___StarOffice_Writer_SGF \ SGV___StarDraw_2_0 \ SVM___StarView_Metafile \ + SVG___Scalable_Vector_Graphics \ TGA___Truevision_TARGA \ TIF___Tag_Image_File \ WMF___MS_Windows_Metafile \ diff --git a/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu b/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu new file mode 100644 index 000000000000..1212e056762e --- /dev/null +++ b/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu @@ -0,0 +1,13 @@ + <node oor:name="SVG - Scalable Vector Graphics" oor:op="replace"> + <prop oor:name="Flags"><value>IMPORT ALIEN USESOPTIONS 3RDPARTYFILTER PREFERRED</value></prop> + <prop oor:name="UIComponent"/> + <prop oor:name="FilterService"><value>com.sun.star.comp.Draw.SVGFilter</value></prop> + <prop oor:name="UserData"><value></value></prop> + <prop oor:name="UIName"> + <value xml:lang="x-default">SVG - Scalable Vector Graphics</value> + </prop> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>svg_Scalable_Vector_Graphics</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="DocumentService"><value>com.sun.star.drawing.DrawingDocument</value></prop> + </node> diff --git a/filter/source/config/fragments/types/svg_Scalable_Vector_Graphics.xcu b/filter/source/config/fragments/types/svg_Scalable_Vector_Graphics.xcu index 37643df4c7ec..8865dee4a831 100644 --- a/filter/source/config/fragments/types/svg_Scalable_Vector_Graphics.xcu +++ b/filter/source/config/fragments/types/svg_Scalable_Vector_Graphics.xcu @@ -1,10 +1,10 @@ <node oor:name="svg_Scalable_Vector_Graphics" oor:op="replace" > - <prop oor:name="DetectService"/> + <prop oor:name="DetectService"><value>com.sun.star.comp.Draw.SVGFilter</value></prop> <prop oor:name="URLPattern"/> <prop oor:name="Extensions"><value>svg</value></prop> <prop oor:name="MediaType"><value>image/svg+xml</value></prop> - <prop oor:name="Preferred"><value>false</value></prop> - <prop oor:name="PreferredFilter"/> + <prop oor:name="Preferred"><value>true</value></prop> + <prop oor:name="PreferredFilter"><value>SVG - Scalable Vector Graphics</value></prop> <prop oor:name="UIName"> <value>SVG - Scalable Vector Graphics</value> </prop> diff --git a/filter/source/svg/b2dellipse.cxx b/filter/source/svg/b2dellipse.cxx new file mode 100644 index 000000000000..8ae97c6bcdb3 --- /dev/null +++ b/filter/source/svg/b2dellipse.cxx @@ -0,0 +1,139 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#include "b2dellipse.hxx" + +#include <osl/diagnose.h> + +#include <basegfx/point/b2dpoint.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> + +#include <rtl/instance.hxx> + +#include <boost/scoped_ptr.hpp> +#include <vector> +#include <algorithm> + +class ImplB2DEllipse +{ + basegfx::B2DPoint maCenter; + basegfx::B2DTuple maRadius; + +public: + ImplB2DEllipse() + : maCenter(0.0f, 0.0f), + maRadius(0.0f, 0.0f) + {} + + ImplB2DEllipse(const ImplB2DEllipse& rToBeCopied) + : maCenter(rToBeCopied.maCenter), + maRadius(rToBeCopied.maRadius) + {} + + ImplB2DEllipse& operator=( const ImplB2DEllipse& rToBeCopied ) + { + maCenter = rToBeCopied.maCenter; + maRadius = rToBeCopied.maRadius; + + return *this; + } + + bool isEqual(const ImplB2DEllipse& rCandidate) const + { + return (maCenter == rCandidate.maCenter) + && (maRadius == rCandidate.maRadius); + } + + basegfx::B2DPoint getCenter() const + { + return maCenter; + } + + void setCenter(const basegfx::B2DPoint& rCenter) + { + maCenter = rCenter; + } + + basegfx::B2DTuple getRadius() const + { + return maRadius; + } + + void setRadius(const basegfx::B2DTuple& rRadius) + { + maRadius = rRadius; + } + + + void transform(const basegfx::B2DHomMatrix& /* rMatrix */) + { + } +}; + +////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + + B2DEllipse::B2DEllipse() + {} + + B2DEllipse::B2DEllipse(const basegfx::B2DPoint& rCenter, const basegfx::B2DTuple& rRadius) + : maCenter(rCenter), maRadius(rRadius) + { + } + + B2DEllipse::~B2DEllipse() + { + } + + bool B2DEllipse::operator==(const B2DEllipse& rEllipse) const + { + return (maCenter == rEllipse.maCenter) && (maRadius == rEllipse.maRadius); + } + + bool B2DEllipse::operator!=(const B2DEllipse& rEllipse) const + { + return !(*this == rEllipse); + } + + basegfx::B2DPoint B2DEllipse::getB2DEllipseCenter() const + { + return maCenter; + } + + void B2DEllipse::setB2DEllipseCenter(const basegfx::B2DPoint& rCenter) + { + maCenter = rCenter; + } + + basegfx::B2DTuple B2DEllipse::getB2DEllipseRadius() const + { + return maRadius; + } + + void B2DEllipse::setB2DEllipseRadius(const basegfx::B2DTuple& rRadius) + { + maRadius = rRadius; + } + + void B2DEllipse::transform(const basegfx::B2DHomMatrix& /* rMatrix */) + { + } +} // end of namespace basegfx + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/filter/source/svg/b2dellipse.hxx b/filter/source/svg/b2dellipse.hxx new file mode 100644 index 000000000000..a63d83974ec5 --- /dev/null +++ b/filter/source/svg/b2dellipse.hxx @@ -0,0 +1,77 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#ifndef _BASEGFX_B2DELLIPSE_HXX +#define _BASEGFX_B2DELLIPSE_HXX + +#include <sal/types.h> + +#include <o3tl/cow_wrapper.hxx> + +#include <basegfx/point/b2dpoint.hxx> + +#include <basegfx/tuple/b2dtuple.hxx> + +#include <basegfx/vector/b2enums.hxx> + +////////////////////////////////////////////////////////////////////////////// +// predeclarations +class ImplB2DEllipse; + +namespace basegfx +{ + class B2DPoint; + class B2DVector; + class B2DHomMatrix; +} // end of namespace basegfx + +////////////////////////////////////////////////////////////////////////////// + +namespace basegfx +{ + class B2DEllipse + { + private: + basegfx::B2DPoint maCenter; + basegfx::B2DTuple maRadius; + + public: + B2DEllipse(); + B2DEllipse(const B2DEllipse& rEllipse); + B2DEllipse(const basegfx::B2DPoint& rCenter, const basegfx::B2DTuple& rRadius); + ~B2DEllipse(); + + // assignment operator + B2DEllipse& operator=(const B2DEllipse& rEllipse); + + // compare operators + bool operator==(const B2DEllipse& rEllipse) const; + bool operator!=(const B2DEllipse& rEllipse) const; + + // Coordinate interface + basegfx::B2DPoint getB2DEllipseCenter() const; + void setB2DEllipseCenter(const basegfx::B2DPoint& rCenter); + + basegfx::B2DTuple getB2DEllipseRadius() const; + void setB2DEllipseRadius(const basegfx::B2DTuple& rRadius); + + // apply transformation given in matrix form to the Ellipse + void transform(const basegfx::B2DHomMatrix& rMatrix); + }; +} // end of namespace basegfx + +////////////////////////////////////////////////////////////////////////////// + +#endif /* _BASEGFX_B2DELLIPSE_HXX */ diff --git a/filter/source/svg/gentoken.pl b/filter/source/svg/gentoken.pl new file mode 100644 index 000000000000..75bb1e262aab --- /dev/null +++ b/filter/source/svg/gentoken.pl @@ -0,0 +1,58 @@ +# from oox/source/token - should really put this into solenv + +$ARGV0 = shift @ARGV; +$ARGV1 = shift @ARGV; +$ARGV2 = shift @ARGV; + +open ( TOKENS, $ARGV0 ) || die "can't open token file: $!"; +my %tokens; + +while ( defined ($line = <TOKENS>) ) +{ + if( !($line =~ /^#/) ) + { + chomp($line); + @token = split(/\s+/,$line); + if ( not defined ($token[1]) ) + { + $token[1] = "XML_".$token[0]; + $token[1] =~ tr/\-\.\:/___/; + $token[1] =~ s/\+/PLUS/g; + $token[1] =~ s/\-/MINUS/g; + } + + $tokens{$token[0]} = uc($token[1]); + } +} +close ( TOKENS ); + +open ( HXX, ">$ARGV1" ) || die "can't open tokens.hxx file: $!"; +open ( GPERF, ">$ARGV2" ) || die "can't open tokens.gperf file: $!"; + +print ( GPERF "%language=C++\n" ); +print ( GPERF "%global-table\n" ); +print ( GPERF "%null-strings\n" ); +print ( GPERF "%struct-type\n" ); +print ( GPERF "struct xmltoken\n" ); +print ( GPERF "{\n" ); +print ( GPERF " const sal_Char *name; sal_Int32 nToken; \n" ); +print ( GPERF "};\n" ); +print ( GPERF "%%\n" ); + +print ( HXX "#ifndef INCLUDED_AUTOGEN_TOKEN_HXX\n" ); +print ( HXX "#define INCLUDED_AUTOGEN_TOKEN_HXX\n\n" ); +print ( HXX "#include <sal/types.h>\n\n" ); + +$i = 0; +foreach( sort(keys(%tokens)) ) +{ + print( HXX "const sal_Int32 $tokens{$_} = $i;\n" ); + print( GPERF "$_,$tokens{$_}\n" ); + $i = $i + 1; +} +print ( GPERF "%%\n" ); +print ( HXX "const sal_Int32 XML_TOKEN_COUNT = $i;\n" ); +print ( HXX "const sal_Int32 XML_TOKEN_INVALID = -1;\n\n" ); +print ( HXX "#endif\n" ); +close ( HXX ); +close ( GPERF ); diff --git a/filter/source/svg/gfxtypes.hxx b/filter/source/svg/gfxtypes.hxx new file mode 100644 index 000000000000..3675e30e65eb --- /dev/null +++ b/filter/source/svg/gfxtypes.hxx @@ -0,0 +1,356 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#ifndef INCLUDED_GFXTYPES_HXX +#define INCLUDED_GFXTYPES_HXX + +#include <basegfx/range/b2drange.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> + +#include <hash_set> +#include <hash_map> +#include <rtl/ustring.hxx> + +namespace svgi +{ + +struct ARGBColor +{ + double toDoubleColor( sal_uInt8 val ) { return val/255.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) + {} + ARGBColor( double r_, double g_, double b_ ) : + a(1.0), r(r_), g(g_), b(b_) + {} + ARGBColor( double a_, double r_, double g_, double b_ ) : + a(a_), r(r_), g(g_), b(b_) + {} + ARGBColor( int r_, int g_, int b_ ) : + a(1.0), + r(toDoubleColor(sal::static_int_cast<sal_uInt8>(r_))), + g(toDoubleColor(sal::static_int_cast<sal_uInt8>(g_))), + b(toDoubleColor(sal::static_int_cast<sal_uInt8>(b_))) + {} + ARGBColor( int a_, int r_, int g_, int b_ ) : + a(toDoubleColor(sal::static_int_cast<sal_uInt8>(a_))), + r(toDoubleColor(sal::static_int_cast<sal_uInt8>(r_))), + g(toDoubleColor(sal::static_int_cast<sal_uInt8>(g_))), + b(toDoubleColor(sal::static_int_cast<sal_uInt8>(b_))) + {} + double a; + double r; + double g; + double b; +}; +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) + {} + ARGBColor maStopColor; + double mnStopPosition; +}; +inline bool operator==( const GradientStop& rLHS, const GradientStop& rRHS ) +{ return rLHS.mnStopPosition==rRHS.mnStopPosition && rLHS.maStopColor==rRHS.maStopColor; } + +struct Gradient +{ + 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 ) +{ + 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 +{ + NONE, + SOLID, + GRADIENT +}; + +enum FillRule +{ + NON_ZERO, + EVEN_ODD +}; + +enum TextAlign +{ + BEFORE, + CENTER, + AFTER +}; + +enum CapStyle +{ + BUTT, + RECT, + ROUND +}; + +enum FontStyle +{ + STYLE_NORMAL, + STYLE_OBLIQUE, + STYLE_ITALIC +}; + +enum FontVariant +{ + VARIANT_NORMAL, + VARIANT_SMALLCAPS +}; + +struct State +{ + State() : + maCTM(), + maTransform(), + maViewport(), + maViewBox(), + maFontFamily(), // app-default + mnFontSize(12.0), + meFontStyle(STYLE_NORMAL), + meFontVariant(VARIANT_NORMAL), + mnFontWeight(400.0), + meTextAnchor(BEFORE), + meTextDisplayAlign(BEFORE), + mnTextLineIncrement(0.0), + maCurrentColor(1.0), + mbVisibility(true), + meFillType(SOLID), + mnFillOpacity(1.0), + meStrokeType(NONE), + mnStrokeOpacity(1.0), + meViewportFillType(NONE), + mnViewportFillOpacity(1.0), + maFillColor(0.0), + maFillGradient(Gradient::LINEAR), + meFillRule(NON_ZERO), + maStrokeColor(0.0), + maStrokeGradient(Gradient::LINEAR), + maDashArray(), + mnDashOffset(0.0), + meLineCap(BUTT), + meLineJoin(basegfx::B2DLINEJOIN_MITER), + mnMiterLimit(4.0), + mnStrokeWidth(1.0), + maViewportFillColor(1.0), + maViewportFillGradient(Gradient::LINEAR), + mnStyleId(0) + {} + + basegfx::B2DHomMatrix maCTM; + basegfx::B2DHomMatrix maTransform; + basegfx::B2DRange maViewport; + basegfx::B2DRange maViewBox; + + rtl::OUString maFontFamily; + /** Absolute: xx-small=6.94 | x-small=8.33 | small=10 | medium=12 | large=14.4 | x-large=17.28 | xx-large=20.736 + + Relative(to parent): larger (enlarge by 1.2) + smaller (shrink by 1.2) + + */ + double mnFontSize; + FontStyle meFontStyle; + FontVariant meFontVariant; + double mnFontWeight; + + TextAlign meTextAnchor; // text-anchor + TextAlign meTextDisplayAlign; // display-align + double mnTextLineIncrement; // 0.0 means auto + + ARGBColor maCurrentColor; + bool mbVisibility; + + PaintType meFillType; + double mnFillOpacity; + PaintType meStrokeType; + double mnStrokeOpacity; + PaintType meViewportFillType; + double mnViewportFillOpacity; + + ARGBColor maFillColor; + Gradient maFillGradient; + FillRule meFillRule; + + ARGBColor maStrokeColor; + Gradient maStrokeGradient; + std::vector<double> maDashArray; + double mnDashOffset; + CapStyle meLineCap; + basegfx::B2DLineJoin meLineJoin; + double mnMiterLimit; + double mnStrokeWidth; + + ARGBColor maViewportFillColor; + Gradient maViewportFillGradient; + + sal_Int32 mnStyleId; +}; + +inline bool operator==(const State& rLHS, const State& rRHS ) +{ + return rLHS.maCTM==rRHS.maCTM && + rLHS.maTransform==rRHS.maTransform && + rLHS.maViewport==rRHS.maViewport && + rLHS.maViewBox==rRHS.maViewBox && + rLHS.maFontFamily==rRHS.maFontFamily && + rLHS.mnFontSize==rRHS.mnFontSize && + rLHS.meFontStyle==rRHS.meFontStyle && + rLHS.meFontVariant==rRHS.meFontVariant && + rLHS.mnFontWeight==rRHS.mnFontWeight && + rLHS.meTextAnchor==rRHS.meTextAnchor && + rLHS.meTextDisplayAlign==rRHS.meTextDisplayAlign && + rLHS.mnTextLineIncrement==rRHS.mnTextLineIncrement && + rLHS.maCurrentColor==rRHS.maCurrentColor && + rLHS.mbVisibility==rRHS.mbVisibility && + rLHS.meFillType==rRHS.meFillType && + rLHS.mnFillOpacity==rRHS.mnFillOpacity && + rLHS.meStrokeType==rRHS.meStrokeType && + rLHS.mnStrokeOpacity==rRHS.mnStrokeOpacity && + rLHS.meViewportFillType==rRHS.meViewportFillType && + rLHS.mnViewportFillOpacity==rRHS.mnViewportFillOpacity && + rLHS.maFillColor==rRHS.maFillColor && + rLHS.maFillGradient==rRHS.maFillGradient && + rLHS.meFillRule==rRHS.meFillRule && + rLHS.maStrokeColor==rRHS.maStrokeColor && + rLHS.maStrokeGradient==rRHS.maStrokeGradient && + rLHS.maDashArray==rRHS.maDashArray && + rLHS.mnDashOffset==rRHS.mnDashOffset && + rLHS.meLineCap==rRHS.meLineCap && + rLHS.meLineJoin==rRHS.meLineJoin && + rLHS.mnMiterLimit==rRHS.mnMiterLimit && + rLHS.mnStrokeWidth==rRHS.mnStrokeWidth && + rLHS.maViewportFillColor==rRHS.maViewportFillColor && + rLHS.maViewportFillGradient==rRHS.maViewportFillGradient; +} + +struct StateHash +{ + size_t operator()(const State& rState ) const + { + return size_t(rState.maCTM.get( 0, 0 )) + ^ size_t(rState.maCTM.get( 1, 0 )) + ^ size_t(rState.maCTM.get( 0, 1 )) + ^ size_t(rState.maCTM.get( 1, 1 )) + ^ size_t(rState.maCTM.get( 0, 2 )) + ^ size_t(rState.maCTM.get( 1, 2 )) + ^ size_t(rState.maViewport.getWidth()) + ^ size_t(rState.maViewport.getHeight()) + ^ size_t(rState.maViewBox.getWidth()) + ^ size_t(rState.maViewBox.getHeight()) + ^ size_t(rState.maFontFamily.hashCode()) + ^ size_t(rState.mnFontSize) + ^ size_t(rState.meFontStyle) + ^ size_t(rState.meFontVariant) + ^ size_t(rState.mnFontWeight) + ^ size_t(rState.meTextAnchor) + ^ size_t(rState.meTextDisplayAlign) + ^ size_t(rState.mnTextLineIncrement) + ^ size_t(rState.mbVisibility) + ^ size_t(rState.meFillType) + ^ size_t(rState.mnFillOpacity) + ^ size_t(rState.meStrokeType) + ^ size_t(rState.mnStrokeOpacity) + ^ size_t(rState.meViewportFillType) + ^ size_t(rState.mnViewportFillOpacity) + ^ size_t(rState.maFillColor.a) + ^ size_t(rState.maFillColor.r) + ^ size_t(rState.maFillColor.g) + ^ size_t(rState.maFillColor.b) + ^ size_t(rState.maFillGradient.maStops.size()) + ^ size_t(rState.meFillRule) + ^ size_t(rState.maStrokeColor.a) + ^ size_t(rState.maStrokeColor.r) + ^ size_t(rState.maStrokeColor.g) + ^ size_t(rState.maStrokeColor.b) + ^ size_t(rState.maStrokeGradient.maStops.size()) + ^ size_t(rState.maDashArray.size()) + ^ size_t(rState.mnDashOffset) + ^ size_t(rState.meLineCap) + ^ size_t(rState.meLineJoin) + ^ size_t(rState.mnMiterLimit) + ^ size_t(rState.mnStrokeWidth) + ^ size_t(rState.maViewportFillColor.a) + ^ size_t(rState.maViewportFillColor.r) + ^ size_t(rState.maViewportFillColor.g) + ^ size_t(rState.maViewportFillColor.b) + ^ size_t(rState.maViewportFillGradient.maStops.size()); + } +}; + +typedef std::hash_set<State, StateHash> StatePool; +typedef std::hash_map<sal_Int32, State> StateMap; + +} // namespace svgi + +#endif diff --git a/filter/source/svg/makefile.mk b/filter/source/svg/makefile.mk index 12c1210c18c4..c9f8e09c8e22 100644 --- a/filter/source/svg/makefile.mk +++ b/filter/source/svg/makefile.mk @@ -26,25 +26,38 @@ #************************************************************************* PRJ=..$/.. + PRJNAME=filter TARGET=svgfilter - ENABLE_EXCEPTIONS=TRUE VISIBILITY_HIDDEN=TRUE # --- Settings ---------------------------------- -.INCLUDE : settings.mk +.INCLUDE : settings.mk +.INCLUDE : libs.mk # --- Types ------------------------------------- -SLOFILES= $(SLO)$/svguno.obj \ - $(SLO)$/svgfilter.obj \ +SLOFILES= \ + $(SLO)$/b2dellipse.obj \ + $(SLO)$/parserfragments.obj \ $(SLO)$/svgexport.obj \ + $(SLO)$/svgfilter.obj \ $(SLO)$/svgfontexport.obj \ - $(SLO)$/svgwriter.obj -.IF "$(SOLAR_JAVA)"!="" -SLOFILES+= $(SLO)$/svgimport.obj + $(SLO)$/svgimport.obj \ + $(SLO)$/svgreader.obj \ + $(SLO)$/svgwriter.obj \ + $(SLO)$/tokenmap.obj \ + $(SLO)$/units.obj + +.IF "$(COMID)"=="gcc3" +.IF "$(CCNUMVER)">="000400000000" || "$(SYSTEM_BOOST)"=="YES" +CFLAGS+=-DUSE_MODERN_SPIRIT +.ENDIF +.ENDIF +.IF "$(SYSTEM_BOOST)"=="NO" +CFLAGS+=-DUSE_MODERN_SPIRIT .ENDIF # --- Library ----------------------------------- @@ -54,21 +67,17 @@ SHL1TARGET=$(TARGET)$(DLLPOSTFIX) SHL1STDLIBS=\ $(EDITENGLIB) \ $(SVXCORELIB) \ + $(BASEGFXLIB) \ $(XMLOFFLIB) \ - $(SVTOOLLIB) \ + $(SVTOOLLIB) \ $(VCLLIB) \ $(UNOTOOLSLIB) \ $(TOOLSLIB) \ $(COMPHELPERLIB) \ $(CPPUHELPERLIB) \ $(CPPULIB) \ - $(SALLIB) - -.IF "$(SOLAR_JAVA)"!="" -SHL1STDLIBS+=\ - $(JVMACCESSLIB) -.ENDIF - + $(SALLIB) \ + $(LIBXML) SHL1DEPN= SHL1IMPLIB= i$(SHL1TARGET) @@ -81,3 +90,16 @@ DEF1NAME=$(SHL1TARGET) # --- Targets ---------------------------------- .INCLUDE : target.mk + +# Generate gperf files - from oox/source/token +$(INCCOM)$/tokens.hxx $(MISC)$/tokens.gperf : tokens.txt gentoken.pl + $(PERL) gentoken.pl tokens.txt $(INCCOM)$/tokens.hxx $(MISC)$/tokens.gperf + +$(INCCOM)$/tokens.cxx : $(MISC)$/tokens.gperf makefile.mk + gperf --compare-strncmp -C -m 20 $(MISC)$/tokens.gperf | $(SED) -e "s/(char\*)0/(char\*)0, 0/g" >$(INCCOM)$/tokens.cxx + +$(SLO)$/tokenmap.obj : $(INCCOM)$/tokens.cxx $(INCCOM)$/tokens.hxx + +$(SLO)$/parserfragments.obj : $(INCCOM)$/tokens.cxx $(INCCOM)$/tokens.hxx + +$(SLO)$/svgreader.obj : $(INCCOM)$/tokens.cxx $(INCCOM)$/tokens.hxx diff --git a/filter/source/svg/parserfragments.cxx b/filter/source/svg/parserfragments.cxx new file mode 100644 index 000000000000..d11ceca02d70 --- /dev/null +++ b/filter/source/svg/parserfragments.cxx @@ -0,0 +1,553 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_filter.hxx" + +#include "parserfragments.hxx" +#include "spirit_supplements.hxx" +#include "gfxtypes.hxx" + +#include <basegfx/tools/canvastools.hxx> +#include <com/sun/star/geometry/AffineMatrix2D.hpp> + +#include <string.h> +#include <limits.h> +#include <boost/bind.hpp> +#include <boost/spirit.hpp> +#include <boost/spirit/dynamic/while.hpp> +#include <numeric> +#include <algorithm> + +#include "units.hxx" +#include "tokenmap.hxx" + +using namespace ::com::sun::star; + +namespace svgi +{ + +inline sal_uInt8 hex2int( char val ) +{ + return val <= '9' ? val-'0' : (val < 'a' ? val+10-'A' : val+10-'a'); +} + +void setFourBitColor( double& rChannel, char nChar ) +{ + const sal_uInt8 nVal(hex2int(nChar)); + OSL_TRACE( "setFourBitCOlor %d color", nVal ); + rChannel = (nVal*16+nVal)/255.0; +} + +void setEightBitColor( double& rChannel, const char* pStart, const char* ) +{ + const sal_uInt8 nVal0(hex2int(pStart[0])); + const sal_uInt8 nVal1(hex2int(pStart[1])); + OSL_TRACE( "setEightbitCOlor %d, %d color", nVal0, nVal1 ); + rChannel = (nVal0*16+nVal1)/255.0; +} + +void setIntColor( double& rChannel, sal_uInt8 nVal ) +{ + OSL_TRACE( "setIntColor %d color", nVal ); + rChannel = nVal/255.0; +} + +void calcRotation(std::vector<geometry::AffineMatrix2D>& rTransforms, + geometry::AffineMatrix2D& rCurrTransform, + double fRotationAngle) +{ + ::basegfx::B2DHomMatrix aCurr; + aCurr.translate(-rCurrTransform.m02,-rCurrTransform.m12); + aCurr.rotate(fRotationAngle*M_PI/180); + aCurr.translate(rCurrTransform.m02,rCurrTransform.m12); + + OSL_TRACE("calcRotation - fRotationAngle - %f", fRotationAngle); + rTransforms.push_back( + basegfx::unotools::affineMatrixFromHomMatrix( + rCurrTransform, + aCurr)); +} + +void calcSkewX(std::vector<geometry::AffineMatrix2D>& rTransforms, + double fSkewAngle) +{ + geometry::AffineMatrix2D aMat(1.0,0.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*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 ) +{ + basegfx::B2DHomMatrix aLHS; + basegfx::B2DHomMatrix aRHS; + + basegfx::unotools::homMatrixFromAffineMatrix(aLHS,rLHS); + basegfx::unotools::homMatrixFromAffineMatrix(aRHS,rRHS); + + aRHS*=aLHS; + + geometry::AffineMatrix2D aRet; + return basegfx::unotools::affineMatrixFromHomMatrix(aRet,aRHS); +} + +bool parseColor( const char* sColor, ARGBColor& rColor ) +{ + using namespace ::boost::spirit; + + int_parser<sal_uInt8,10,1,3> byte_p; + + if( parse(sColor, + // Begin grammar + ( + // the #rrggbb form + ('#' >> (xdigit_p >> xdigit_p)[boost::bind(&setEightBitColor, + boost::ref(rColor.r),_1,_2)] + >> (xdigit_p >> xdigit_p)[boost::bind(&setEightBitColor, + boost::ref(rColor.g),_1,_2)] + >> (xdigit_p >> xdigit_p)[boost::bind(&setEightBitColor, + boost::ref(rColor.b),_1,_2)]) + | + // the #rgb form + ('#' >> xdigit_p[boost::bind(&setFourBitColor, + boost::ref(rColor.r),_1)] + >> xdigit_p[boost::bind(&setFourBitColor, + boost::ref(rColor.g),_1)] + >> xdigit_p[boost::bind(&setFourBitColor, + boost::ref(rColor.b),_1)]) + | + // rgb() form + (str_p("rgb") + >> '(' >> + ( + // rgb(int,int,int) + (byte_p[boost::bind(&setIntColor, + boost::ref(rColor.r),_1)] >> ',' >> + byte_p[boost::bind(&setIntColor, + boost::ref(rColor.g),_1)] >> ',' >> + byte_p[boost::bind(&setIntColor, + boost::ref(rColor.b),_1)]) + | + // rgb(double,double,double) + (real_p[assign_a(rColor.r)] >> ',' >> + real_p[assign_a(rColor.g)] >> ',' >> + real_p[assign_a(rColor.b)]) + ) + >> ')') + ) >> end_p, + // End grammar + space_p).full ) + { + // free-form color found & parsed + return true; + } + + // no free-form color - maybe a color name? + // 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; + case XML_AQUA: rColor = ARGBColor(0,255,255); return true; + case XML_AQUAMARINE: rColor = ARGBColor(127,255,212); return true; + case XML_AZURE: rColor = ARGBColor(240,255,255); return true; + case XML_BEIGE: rColor = ARGBColor(245,245,220); return true; + case XML_BISQUE: rColor = ARGBColor(255,228,196); return true; + case XML_BLACK: rColor = ARGBColor(0,0,0); return true; + case XML_BLANCHEDALMOND: rColor = ARGBColor(255,235,205); return true; + case XML_BLUE: rColor = ARGBColor(0,0,255); return true; + case XML_BLUEVIOLET: rColor = ARGBColor(138,43,226); return true; + case XML_BROWN: rColor = ARGBColor(165,42,42); return true; + case XML_BURLYWOOD: rColor = ARGBColor(222,184,135); return true; + case XML_CADETBLUE: rColor = ARGBColor(95,158,160); return true; + case XML_CHARTREUSE: rColor = ARGBColor(127,255,0); return true; + case XML_CHOCOLATE: rColor = ARGBColor(210,105,30); return true; + case XML_CORAL: rColor = ARGBColor(255,127,80); return true; + case XML_CORNFLOWERBLUE: rColor = ARGBColor(100,149,237); return true; + case XML_CORNSILK: rColor = ARGBColor(255,248,220); return true; + case XML_CRIMSON: rColor = ARGBColor(220,20,60); return true; + case XML_CYAN: rColor = ARGBColor(0,255,255); return true; + case XML_DARKBLUE: rColor = ARGBColor(0,0,139); return true; + case XML_DARKCYAN: rColor = ARGBColor(0,139,139); return true; + case XML_DARKGOLDENROD: rColor = ARGBColor(184,134,11); return true; + case XML_DARKGRAY: rColor = ARGBColor(169,169,169); return true; + case XML_DARKGREEN: rColor = ARGBColor(0,100,0); return true; + case XML_DARKGREY: rColor = ARGBColor(169,169,169); return true; + case XML_DARKKHAKI: rColor = ARGBColor(189,183,107); return true; + case XML_DARKMAGENTA: rColor = ARGBColor(139,0,139); return true; + case XML_DARKOLIVEGREEN: rColor = ARGBColor(85,107,47); return true; + case XML_DARKORANGE: rColor = ARGBColor(255,140,0); return true; + case XML_DARKORCHID: rColor = ARGBColor(153,50,204); return true; + case XML_DARKRED: rColor = ARGBColor(139,0,0); return true; + case XML_DARKSALMON: rColor = ARGBColor(233,150,122); return true; + case XML_DARKSEAGREEN: rColor = ARGBColor(143,188,143); return true; + case XML_DARKSLATEBLUE: rColor = ARGBColor(72,61,139); return true; + case XML_DARKSLATEGRAY: rColor = ARGBColor(47,79,79); return true; + case XML_DARKSLATEGREY: rColor = ARGBColor(47,79,79); return true; + case XML_DARKTURQUOISE: rColor = ARGBColor(0,206,209); return true; + case XML_DARKVIOLET: rColor = ARGBColor(148,0,211); return true; + case XML_DEEPPINK: rColor = ARGBColor(255,20,147); return true; + case XML_DEEPSKYBLUE: rColor = ARGBColor(0,191,255); return true; + case XML_DIMGRAY: rColor = ARGBColor(105,105,105); return true; + case XML_DIMGREY: rColor = ARGBColor(105,105,105); return true; + case XML_DODGERBLUE: rColor = ARGBColor(30,144,255); return true; + case XML_FIREBRICK: rColor = ARGBColor(178,34,34); return true; + case XML_FLORALWHITE: rColor = ARGBColor(255,250,240); return true; + case XML_FORESTGREEN: rColor = ARGBColor(34,139,34); return true; + case XML_FUCHSIA: rColor = ARGBColor(255,0,255); return true; + case XML_GAINSBORO: rColor = ARGBColor(220,220,220); return true; + case XML_GHOSTWHITE: rColor = ARGBColor(248,248,255); return true; + case XML_GOLD: rColor = ARGBColor(255,215,0); return true; + case XML_GOLDENROD: rColor = ARGBColor(218,165,32); return true; + case XML_GRAY: rColor = ARGBColor(128,128,128); return true; + case XML_GREY: rColor = ARGBColor(128,128,128); return true; + case XML_GREEN: rColor = ARGBColor(0,128,0); return true; + case XML_GREENYELLOW: rColor = ARGBColor(173,255,47); return true; + case XML_HONEYDEW: rColor = ARGBColor(240,255,240); return true; + case XML_HOTPINK: rColor = ARGBColor(255,105,180); return true; + case XML_INDIANRED: rColor = ARGBColor(205,92,92); return true; + case XML_INDIGO: rColor = ARGBColor(75,0,130); return true; + case XML_IVORY: rColor = ARGBColor(255,255,240); return true; + case XML_KHAKI: rColor = ARGBColor(240,230,140); return true; + case XML_LAVENDER: rColor = ARGBColor(230,230,250); return true; + case XML_LAVENDERBLUSH: rColor = ARGBColor(255,240,245); return true; + case XML_LAWNGREEN: rColor = ARGBColor(124,252,0); return true; + case XML_LEMONCHIFFON: rColor = ARGBColor(255,250,205); return true; + case XML_LIGHTBLUE: rColor = ARGBColor(173,216,230); return true; + case XML_LIGHTCORAL: rColor = ARGBColor(240,128,128); return true; + case XML_LIGHTCYAN: rColor = ARGBColor(224,255,255); return true; + case XML_LIGHTGOLDENRODYELLOW: rColor = ARGBColor(250,250,210); return true; + case XML_LIGHTGRAY: rColor = ARGBColor(211,211,211); return true; + case XML_LIGHTGREEN: rColor = ARGBColor(144,238,144); return true; + case XML_LIGHTGREY: rColor = ARGBColor(211,211,211); return true; + case XML_LIGHTPINK: rColor = ARGBColor(255,182,193); return true; + case XML_LIGHTSALMON: rColor = ARGBColor(255,160,122); return true; + case XML_LIGHTSEAGREEN: rColor = ARGBColor(32,178,170); return true; + case XML_LIGHTSKYBLUE: rColor = ARGBColor(135,206,250); return true; + case XML_LIGHTSLATEGRAY: rColor = ARGBColor(119,136,153); return true; + case XML_LIGHTSLATEGREY: rColor = ARGBColor(119,136,153); return true; + case XML_LIGHTSTEELBLUE: rColor = ARGBColor(176,196,222); return true; + case XML_LIGHTYELLOW: rColor = ARGBColor(255,255,224); return true; + case XML_LIME: rColor = ARGBColor(0,255,0); return true; + case XML_LIMEGREEN: rColor = ARGBColor(50,205,50); return true; + case XML_LINEN: rColor = ARGBColor(250,240,230); return true; + case XML_MAGENTA: rColor = ARGBColor(255,0,255); return true; + case XML_MAROON: rColor = ARGBColor(128,0,0); return true; + case XML_MEDIUMAQUAMARINE: rColor = ARGBColor(102,205,170); return true; + case XML_MEDIUMBLUE: rColor = ARGBColor(0,0,205); return true; + case XML_MEDIUMORCHID: rColor = ARGBColor(186,85,211); return true; + case XML_MEDIUMPURPLE: rColor = ARGBColor(147,112,219); return true; + case XML_MEDIUMSEAGREEN: rColor = ARGBColor(60,179,113); return true; + case XML_MEDIUMSLATEBLUE: rColor = ARGBColor(123,104,238); return true; + case XML_MEDIUMSPRINGGREEN: rColor = ARGBColor(0,250,154); return true; + case XML_MEDIUMTURQUOISE: rColor = ARGBColor(72,209,204); return true; + case XML_MEDIUMVIOLETRED: rColor = ARGBColor(199,21,133); return true; + case XML_MIDNIGHTBLUE: rColor = ARGBColor(25,25,112); return true; + case XML_MINTCREAM: rColor = ARGBColor(245,255,250); return true; + case XML_MISTYROSE: rColor = ARGBColor(255,228,225); return true; + case XML_MOCCASIN: rColor = ARGBColor(255,228,181); return true; + case XML_NAVAJOWHITE: rColor = ARGBColor(255,222,173); return true; + case XML_NAVY: rColor = ARGBColor(0,0,128); return true; + case XML_OLDLACE: rColor = ARGBColor(253,245,230); return true; + case XML_OLIVE: rColor = ARGBColor(128,128,0); return true; + case XML_OLIVEDRAB: rColor = ARGBColor(107,142,35); return true; + case XML_ORANGE: rColor = ARGBColor(255,165,0); return true; + case XML_ORANGERED: rColor = ARGBColor(255,69,0); return true; + case XML_ORCHID: rColor = ARGBColor(218,112,214); return true; + case XML_PALEGOLDENROD: rColor = ARGBColor(238,232,170); return true; + case XML_PALEGREEN: rColor = ARGBColor(152,251,152); return true; + case XML_PALETURQUOISE: rColor = ARGBColor(175,238,238); return true; + case XML_PALEVIOLETRED: rColor = ARGBColor(219,112,147); return true; + case XML_PAPAYAWHIP: rColor = ARGBColor(255,239,213); return true; + case XML_PEACHPUFF: rColor = ARGBColor(255,218,185); return true; + case XML_PERU: rColor = ARGBColor(205,133,63); return true; + case XML_PINK: rColor = ARGBColor(255,192,203); return true; + case XML_PLUM: rColor = ARGBColor(221,160,221); return true; + case XML_POWDERBLUE: rColor = ARGBColor(176,224,230); return true; + case XML_PURPLE: rColor = ARGBColor(128,0,128); return true; + case XML_RED: rColor = ARGBColor(255,0,0); return true; + case XML_ROSYBROWN: rColor = ARGBColor(188,143,143); return true; + case XML_ROYALBLUE: rColor = ARGBColor(65,105,225); return true; + case XML_SADDLEBROWN: rColor = ARGBColor(139,69,19); return true; + case XML_SALMON: rColor = ARGBColor(250,128,114); return true; + case XML_SANDYBROWN: rColor = ARGBColor(244,164,96); return true; + case XML_SEAGREEN: rColor = ARGBColor(46,139,87); return true; + case XML_SEASHELL: rColor = ARGBColor(255,245,238); return true; + case XML_SIENNA: rColor = ARGBColor(160,82,45); return true; + case XML_SILVER: rColor = ARGBColor(192,192,192); return true; + case XML_SKYBLUE: rColor = ARGBColor(135,206,235); return true; + case XML_SLATEBLUE: rColor = ARGBColor(106,90,205); return true; + case XML_SLATEGRAY: rColor = ARGBColor(112,128,144); return true; + case XML_SLATEGREY: rColor = ARGBColor(112,128,144); return true; + case XML_SNOW: rColor = ARGBColor(255,250,250); return true; + case XML_SPRINGGREEN: rColor = ARGBColor(0,255,127); return true; + case XML_STEELBLUE: rColor = ARGBColor(70,130,180); return true; + case XML_TAN: rColor = ARGBColor(210,180,140); return true; + case XML_TEAL: rColor = ARGBColor(0,128,128); return true; + case XML_THISTLE: rColor = ARGBColor(216,191,216); return true; + case XML_TOMATO: rColor = ARGBColor(255,99,71); return true; + case XML_TURQUOISE: rColor = ARGBColor(64,224,208); return true; + case XML_VIOLET: rColor = ARGBColor(238,130,238); return true; + case XML_WHEAT: rColor = ARGBColor(245,222,179); return true; + case XML_WHITE: rColor = ARGBColor(255,255,255); return true; + case XML_WHITESMOKE: rColor = ARGBColor(245,245,245); return true; + case XML_YELLOW: rColor = ARGBColor(255,255,0); return true; + case XML_YELLOWGREEN: rColor = ARGBColor(154,205,50); return true; + + default: + return false; // no color at all, I'd guess. + } +} + +bool parseOpacity (const char* sOpacity, ARGBColor& rColor ) +{ + using namespace ::boost::spirit; + + if( parse(sOpacity, + // Begin grammar + ( + real_p[assign_a(rColor.a)] + ) >> end_p, + // End grammar + space_p).full ) + { + return true; + } + return false; +} + +////////////////////////////////////////////////////////////// + +bool parseTransform( const char* sTransform, basegfx::B2DHomMatrix& rTransform ) +{ + using namespace ::boost::spirit; + + double fRefOffsetX(0.0); + double fRefOffsetY(0.0); + bool bRefTransform(false); + + double fRotationAngle=0.0; + double fSkewAngle=0.0; + geometry::AffineMatrix2D aIdentityTransform; + geometry::AffineMatrix2D aCurrTransform; + std::vector<geometry::AffineMatrix2D> aTransforms; + aIdentityTransform.m00 = 1.0; aIdentityTransform.m11 = 1.0; + aCurrTransform = aIdentityTransform; + + const bool bRes = parse(sTransform, + // Begin grammar + ( + // identity transform + str_p("none") + | + // the ref() form + (str_p("ref") + >> '(' + >> str_p("svg")[assign_a(bRefTransform,true)] + >> !(real_p[assign_a(fRefOffsetX)] >> (',' | eps_p) >> + real_p[assign_a(fRefOffsetY)]) + >> ')') + | + // the transform-list form + (list_p( + ( + // matrix(a,b,c,d,e,f) + (str_p("matrix") + >> '(' + >> 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[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[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)] + >> !((',' | 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") + >> '(' + >> real_p[assign_a(fSkewAngle)] + >> ')')[boost::bind(&calcSkewX, + boost::ref(aTransforms), + boost::cref(fSkewAngle))] + | + // skewY(phi) + (str_p("skewY") + >> '(' + >> real_p[assign_a(fSkewAngle)] + >> ')')[boost::bind(&calcSkewY, + boost::ref(aTransforms), + boost::cref(fSkewAngle))] + // reset current transform after every push + )[assign_a(aCurrTransform,aIdentityTransform)], + // list delimiter is either ',' or space + ',' | eps_p )) + ) >> end_p, + // End grammar + space_p).full; + + if( !bRes ) + return false; + + // fold all transformations into one + const geometry::AffineMatrix2D aTotalTransform( + std::accumulate(aTransforms.begin(), + aTransforms.end(), + aIdentityTransform, + &multiplyMatrix)); + + basegfx::unotools::homMatrixFromAffineMatrix( + rTransform, + aTotalTransform); + + // TODO(F1): handle the ref case + return bRes; +} + +////////////////////////////////////////////////////////////// + +bool parseViewBox( const char* sViewbox, basegfx::B2DRange& rRect ) +{ + using namespace ::boost::spirit; + + double x=0.0,y=0.0,w=0.0,h=0.0; + + const bool bRes = parse(sViewbox, + // Begin grammar + ( + // 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; + + if( !bRes ) + return false; + + rRect = basegfx::B2DRange(x,y,x+w,y+h); + + return true; +} + +////////////////////////////////////////////////////////////// + +bool parseDashArray( const char* sDashArray, std::vector<double>& rOutputVector ) +{ + using namespace ::boost::spirit; + + rOutputVector.clear(); + return parse(sDashArray, + // Begin grammar + ( + // 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; +} + +////////////////////////////////////////////////////////////// + +namespace +{ +void appendChar( std::string& str, char character) +{ + str.append(1,character); +} +} + +bool parseXlinkHref( const char* sXlinkHref, std::string& data ) +{ + using namespace ::boost::spirit; + + data.erase(data.begin(),data.end()); + + std::string sLink(sXlinkHref); + + if (!sLink.compare(0,5,"data:")) + { + // the inplace "data" uri + size_t position = sLink.rfind(','); + if (position > 0 && position < std::string::npos) + { + data = sLink.substr(position+1); + OSL_TRACE("%s", data.c_str()); + return true; + } + } + + return false; +} + +} // namespace svgi diff --git a/filter/source/svg/parserfragments.hxx b/filter/source/svg/parserfragments.hxx new file mode 100644 index 000000000000..bcb6427aa4d8 --- /dev/null +++ b/filter/source/svg/parserfragments.hxx @@ -0,0 +1,50 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#ifndef INCLUDED_PARSERFRAGMENTS_HXX +#define INCLUDED_PARSERFRAGMENTS_HXX + +#include <sal/config.h> +#include <vector> +#include <string> + +namespace basegfx +{ + class B2DHomMatrix; + class B2DRange; +} +namespace svgi +{ + struct ARGBColor; + + /// Parse given string for one of the SVG color grammars + bool parseColor( const char* sColor, ARGBColor& rColor ); + bool parseOpacity( const char* sOpacity, ARGBColor& rColor ); + + /// Parse given string for one of the SVG transformation grammars + bool parseTransform( const char* sTransform, basegfx::B2DHomMatrix& rTransform ); + + /// Parse given string for the viewBox attribute + bool parseViewBox( const char* sViewbox, basegfx::B2DRange& rRect ); + + /// Parse given string for a list of double values, comma-delimited + bool parseDashArray( const char* sDashArray, std::vector<double>& rOutputVector ); + + /// Parse given string for the xlink attribute + bool parseXlinkHref( const char* xlink, std::string& data ); + +} // namespace svgi + +#endif diff --git a/filter/source/svg/spirit_supplements.hxx b/filter/source/svg/spirit_supplements.hxx new file mode 100644 index 000000000000..6623e6d64f3b --- /dev/null +++ b/filter/source/svg/spirit_supplements.hxx @@ -0,0 +1,115 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#ifndef INCLUDED_SPIRIT_SUPPLEMENTS_HXX +#define INCLUDED_SPIRIT_SUPPLEMENTS_HXX + +# ifndef USE_MODERN_SPIRIT +# include <boost/spirit.hpp> + +namespace boost { namespace spirit +{ + template <> + class assign_actor< std::pair<const char*,const char*> > + { + public: + explicit assign_actor(std::pair<const char*,const char*>& ref_) + : ref(ref_) {} + + template <typename T2> + void operator()(T2 const& val) const + { ref = val; } + + template <typename IteratorT> + void operator()(IteratorT const& f, IteratorT const& l) const + { + ref.first = f, ref.second = l; + } + + private: + std::pair<const char*,const char*>& ref; + }; + + template<typename Target, typename Value> struct assigner + { + assigner( Target& rTarget, Value aValue ) : + mrTarget(rTarget), maValue(aValue) + {} + + void assign() const { mrTarget=maValue; } + + void operator()() const { assign(); } + template<typename T1> void operator()(T1) const { assign(); } + template<typename T1,typename T2> void operator()(T1,T2) const { assign(); } + template<typename T1,typename T2,typename T3> void operator()(T1,T2,T3) const { assign(); } + + Target& mrTarget; + const Value maValue; + }; + + template<typename Target, typename Value> inline assigner<Target,Value> + assign_a( Target& rTarget, Value aValue ) + { + return assigner<Target,Value>(rTarget,aValue); + } + + template <typename Target> inline assign_actor<Target> + assign_a(Target& rTarget) + { + return assign_actor<Target>(rTarget); + } + + template<typename Target, typename Value> struct back_pusher + { + back_pusher( Target& rTarget, const Value& rValue ) : + mrTarget(rTarget), mrValue(rValue) + {} + + void push_back() const { mrTarget.push_back(mrValue); } + + void operator()() const { push_back(); } + template<typename T1> void operator()(T1) const { push_back(); } + template<typename T1,typename T2> void operator()(T1,T2) const { push_back(); } + template<typename T1,typename T2,typename T3> void operator()(T1,T2,T3) const { push_back(); } + + Target& mrTarget; + const Value& mrValue; + }; + + template<typename Target, typename Value> inline back_pusher<Target,Value> + push_back_a( Target& rTarget, const Value& rValue ) + { + return back_pusher<Target,Value>(rTarget,rValue); + } + + template<typename Target> struct value_back_pusher + { + explicit value_back_pusher( Target& rTarget ) : + mrTarget(rTarget) + {} + template<typename T1> void operator()(T1 val) const { mrTarget.push_back(val); } + + Target& mrTarget; + }; + + template<typename Target> inline value_back_pusher<Target> + push_back_a( Target& rTarget ) + { + return value_back_pusher<Target>(rTarget); + } +} } + +# endif +#endif diff --git a/filter/source/svg/svgfilter.cxx b/filter/source/svg/svgfilter.cxx index 118474bbe2b0..2755dfcf45d4 100644 --- a/filter/source/svg/svgfilter.cxx +++ b/filter/source/svg/svgfilter.cxx @@ -30,13 +30,16 @@ #include <cstdio> -#include "svgfilter.hxx" +#include <comphelper/servicedecl.hxx> +#include <uno/environment.h> #include <com/sun/star/drawing/XDrawPage.hpp> #include <com/sun/star/drawing/XDrawView.hpp> #include <com/sun/star/frame/XDesktop.hdl> #include <com/sun/star/frame/XController.hdl> #include <vos/mutex.hxx> +#include "svgfilter.hxx" + using ::rtl::OUString; using namespace ::com::sun::star; @@ -44,8 +47,9 @@ using namespace ::com::sun::star; // - SVGFilter - // ------------- -SVGFilter::SVGFilter( const Reference< XMultiServiceFactory > &rxMSF ) : - mxMSF( rxMSF ), +SVGFilter::SVGFilter( const Reference< XComponentContext >& rxCtx ) : + mxMSF( rxCtx->getServiceManager(), + uno::UNO_QUERY_THROW ), mpSVGDoc( NULL ), mpSVGExport( NULL ), mpSVGFontExport( NULL ), @@ -80,11 +84,9 @@ sal_Bool SAL_CALL SVGFilter::filter( const Sequence< PropertyValue >& rDescripto if( pFocusWindow ) pFocusWindow->EnterWait(); -#ifdef SOLAR_JAVA if( mxDstDoc.is() ) bRet = implImport( rDescriptor ); else -#endif if( mxSrcDoc.is() ) { uno::Reference< frame::XDesktop > xDesktop( mxMSF->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.frame.Desktop" ) ), @@ -153,77 +155,64 @@ void SAL_CALL SVGFilter::setSourceDocument( const Reference< XComponent >& xDoc // ----------------------------------------------------------------------------- -#ifdef SOLAR_JAVA void SAL_CALL SVGFilter::setTargetDocument( const Reference< XComponent >& xDoc ) throw (::com::sun::star::lang::IllegalArgumentException, RuntimeException) { mxDstDoc = xDoc; } -#endif - -// ----------------------------------------------------------------------------- - -void SAL_CALL SVGFilter::initialize( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& /* aArguments */ ) - throw (Exception, RuntimeException) -{ -} - -// ----------------------------------------------------------------------------- - -OUString SVGFilter_getImplementationName () - throw (RuntimeException) -{ - return OUString ( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.comp.Draw.SVGFilter" ) ); -} - -// ----------------------------------------------------------------------------- - -#define SERVICE_NAME "com.sun.star.document.SVGFilter" - -sal_Bool SAL_CALL SVGFilter_supportsService( const OUString& ServiceName ) - throw (RuntimeException) -{ - return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) ); -} // ----------------------------------------------------------------------------- -Sequence< OUString > SAL_CALL SVGFilter_getSupportedServiceNames( ) throw (RuntimeException) +rtl::OUString SAL_CALL SVGFilter::detect( Sequence< PropertyValue >& io_rDescriptor ) throw (RuntimeException) { - Sequence < OUString > aRet(1); - OUString* pArray = aRet.getArray(); - pArray[0] = OUString ( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); - return aRet; -} + uno::Reference< io::XInputStream > xInput; + rtl::OUString aURL; -#undef SERVICE_NAME + const beans::PropertyValue* pAttribs = io_rDescriptor.getConstArray(); + const sal_Int32 nAttribs = io_rDescriptor.getLength(); + for( sal_Int32 i = 0; i < nAttribs; i++ ) + { + if( pAttribs[i].Name.equalsAscii( "InputStream" ) ) + pAttribs[i].Value >>= xInput; + } -// ----------------------------------------------------------------------------- + if( !xInput.is() ) + return rtl::OUString(); -Reference< XInterface > SAL_CALL SVGFilter_createInstance( const Reference< XMultiServiceFactory > & rSMgr) throw( Exception ) -{ - return (cppu::OWeakObject*) new SVGFilter( rSMgr ); -} + uno::Reference< io::XSeekable > xSeek( xInput, uno::UNO_QUERY ); + if( xSeek.is() ) + xSeek->seek( 0 ); -// ----------------------------------------------------------------------------- + // read the first 1024 bytes & check a few magic string + // constants (heuristically) + const sal_Int32 nLookAhead = 1024; + uno::Sequence< sal_Int8 > aBuf( nLookAhead ); + const sal_uInt64 nBytes=xInput->readBytes(aBuf, nLookAhead); + const sal_Int8* const pBuf=aBuf.getConstArray(); -OUString SAL_CALL SVGFilter::getImplementationName( ) - throw (RuntimeException) -{ - return SVGFilter_getImplementationName(); -} + sal_Int8 aMagic1[] = {'<', 's', 'v', 'g'}; + if( std::search(pBuf, pBuf+nBytes, + aMagic1, aMagic1+sizeof(aMagic1)/sizeof(*aMagic1)) != pBuf+nBytes ) + return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("svg_Scalable_Vector_Graphics") ); -// ----------------------------------------------------------------------------- + sal_Int8 aMagic2[] = {'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 's', 'v', 'g'}; + if( std::search(pBuf, pBuf+nBytes, + aMagic2, aMagic2+sizeof(aMagic2)/sizeof(*aMagic2)) != pBuf+nBytes ) + return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("svg_Scalable_Vector_Graphics") ); -sal_Bool SAL_CALL SVGFilter::supportsService( const OUString& rServiceName ) - throw (RuntimeException) -{ - return SVGFilter_supportsService( rServiceName ); + return rtl::OUString(); } // ----------------------------------------------------------------------------- -::com::sun::star::uno::Sequence< OUString > SAL_CALL SVGFilter::getSupportedServiceNames( ) throw (RuntimeException) -{ - return SVGFilter_getSupportedServiceNames(); -} +namespace sdecl = comphelper::service_decl; + sdecl::class_<SVGFilter> serviceImpl; + const sdecl::ServiceDecl svgFilter( + serviceImpl, + "com.sun.star.comp.Draw.SVGFilter", + "com.sun.star.document.ImportFilter;" + "com.sun.star.document.ExportFilter;" + "com.sun.star.document.ExtendedTypeDetection" ); + +// The C shared lib entry points +COMPHELPER_SERVICEDECL_EXPORTS1(svgFilter) diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx index 0bbc64e2c244..a1582b5e434f 100644 --- a/filter/source/svg/svgfilter.hxx +++ b/filter/source/svg/svgfilter.hxx @@ -35,20 +35,14 @@ #include <com/sun/star/drawing/XMasterPagesSupplier.hpp> #include <com/sun/star/presentation/XPresentationSupplier.hpp> #include <com/sun/star/document/XFilter.hpp> -#ifdef SOLAR_JAVA #include <com/sun/star/document/XImporter.hpp> -#endif // SOLAR_JAVA #include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> #include <com/sun/star/lang/XInitialization.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/lang/XComponent.hpp> -#include <cppuhelper/implbase1.hxx> -#ifdef SOLAR_JAVA -#include <cppuhelper/implbase5.hxx> -#else // !SOLAR_JAVA #include <cppuhelper/implbase4.hxx> -#endif #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/io/XActiveDataSource.hpp> #include <com/sun/star/presentation/AnimationEffect.hpp> @@ -175,18 +169,10 @@ class SVGFontExport; class SVGActionWriter; class EditFieldInfo; -#ifdef SOLAR_JAVA -class SVGFilter : public cppu::WeakImplHelper5 < XFilter, - XImporter, - XExporter, - XInitialization, - XServiceInfo > -#else // !SOLAR_JAVA class SVGFilter : public cppu::WeakImplHelper4 < XFilter, + XImporter, XExporter, - XInitialization, - XServiceInfo > -#endif + XExtendedFilterDetection > { typedef ::std::hash_map< Reference< XInterface >, ObjectRepresentation, HashReferenceXInterface > ObjectMap; @@ -203,15 +189,11 @@ private: ObjectMap* mpObjects; Reference< XComponent > mxSrcDoc; -#ifdef SOLAR_JAVA Reference< XComponent > mxDstDoc; -#endif Reference< XDrawPage > mxDefaultPage; Link maOldFieldHdl; -#ifdef SOLAR_JAVA sal_Bool implImport( const Sequence< PropertyValue >& rDescriptor ) throw (RuntimeException); -#endif sal_Bool implExport( const Sequence< PropertyValue >& rDescriptor ) throw (RuntimeException); Reference< XDocumentHandler > implCreateExportDocumentHandler( const Reference< XOutputStream >& rxOStm ); @@ -250,26 +232,19 @@ protected: virtual sal_Bool SAL_CALL filter( const Sequence< PropertyValue >& rDescriptor ) throw(RuntimeException); virtual void SAL_CALL cancel( ) throw (RuntimeException); -#ifdef SOLAR_JAVA // XImporter virtual void SAL_CALL setTargetDocument( const Reference< XComponent >& xDoc ) throw(IllegalArgumentException, RuntimeException); -#endif // XExporter virtual void SAL_CALL setSourceDocument( const Reference< XComponent >& xDoc ) throw(IllegalArgumentException, RuntimeException); - // XInitialization - virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) throw(Exception, RuntimeException); - - // XServiceInfo - virtual ::rtl::OUString SAL_CALL getImplementationName() throw(RuntimeException); - virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw(RuntimeException); - virtual Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw(RuntimeException); + // XExtendedFilterDetection + virtual rtl::OUString SAL_CALL detect( Sequence< PropertyValue >& io_rDescriptor ) throw (RuntimeException); public: - SVGFilter( const Reference< XMultiServiceFactory > &rxMSF ); - virtual ~SVGFilter(); + explicit SVGFilter( const Reference< XComponentContext >& rxCtx ); + virtual ~SVGFilter(); }; // ----------------------------------------------------------------------------- diff --git a/filter/source/svg/svgimport.cxx b/filter/source/svg/svgimport.cxx index fd4f711cb085..7f4be27745b6 100644 --- a/filter/source/svg/svgimport.cxx +++ b/filter/source/svg/svgimport.cxx @@ -29,160 +29,59 @@ #include "precompiled_filter.hxx" #include "svgfilter.hxx" +#include "svgreader.hxx" + #include "rtl/ref.hxx" -#include "jvmaccess/virtualmachine.hxx" -// ------------- -// - SVGFilter - -// ------------- + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <com/sun/star/lang/XComponent.hpp> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Type.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/XImportFilter.hpp> + +#include <com/sun/star/io/XActiveDataSource.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> + +using namespace ::com::sun::star; +using namespace ::svgi; sal_Bool SVGFilter::implImport( const Sequence< PropertyValue >& rDescriptor ) throw (RuntimeException) { - Reference< XMultiServiceFactory > xServiceFactory( ::comphelper::getProcessServiceFactory() ) ; - rtl::OUString aTmpFileName; - String aFileName; - sal_Int32 nLength = rDescriptor.getLength(); - const PropertyValue* pValue = rDescriptor.getConstArray(); - sal_Bool bRet = sal_False; - - for( sal_Int32 i = 0 ; ( i < nLength ) && !aTmpFileName.getLength(); i++) - if( pValue[ i ].Name.equalsAscii( "FileName" ) ) - pValue[ i ].Value >>= aTmpFileName; - - if( aTmpFileName.getLength() && xServiceFactory.is() ) + rtl::OUString aURL; + uno::Reference< io::XInputStream > xInputStream; + uno::Reference< task::XStatusIndicator > xStatus; + const sal_Int32 nLength = rDescriptor.getLength(); + const beans::PropertyValue* pAttribs = rDescriptor.getConstArray(); + for ( sal_Int32 i=0 ; i<nLength; ++i, ++pAttribs ) { - - Reference< XJavaVM > xJavaVM( xServiceFactory->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.java.JavaVirtualMachine") ) ), UNO_QUERY ); - Sequence< sal_Int8 > aProcessID( 17 ); - String aLocalFile; - - if( ::utl::LocalFileHelper::ConvertURLToPhysicalName( aTmpFileName, aLocalFile ) && aLocalFile.Len() ) + if( pAttribs->Name.equalsAscii( "InputStream" ) ) { - rtl_getGlobalProcessId( (sal_uInt8 *) aProcessID.getArray() ); - aProcessID[16] = 0; - - OSL_ENSURE(sizeof (sal_Int64) - >= sizeof (jvmaccess::VirtualMachine *), - "Pointer cannot be represented as sal_Int64"); - sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( - static_cast< jvmaccess::VirtualMachine * >(0)); - xJavaVM->getJavaVM(aProcessID) >>= nPointer; - rtl::Reference<jvmaccess::VirtualMachine> _virtualMachine = - reinterpret_cast< jvmaccess::VirtualMachine * >(nPointer); - if (!_virtualMachine.is()) - return bRet; - - jobjectArray aArgs; - jclass aClass; - jmethodID aMId; - jstring aJStr; - - try - { - jvmaccess::VirtualMachine::AttachGuard vmGuard(_virtualMachine); - - JNIEnv * pEnv = vmGuard.getEnvironment(); - - aClass = pEnv->FindClass( "SOTranscoder" ); - - if( aClass ) - { - aMId = pEnv->GetStaticMethodID( aClass, "main", "([Ljava/lang/String;)V" ); - if ( aMId ) - { - - ::utl::TempFile aTempFile; - String aOutputURL( aTempFile.GetURL() ); - String aOutputFile; - - aTempFile.EnableKillingFile(); - - if( ::utl::LocalFileHelper::ConvertURLToPhysicalName( aOutputURL, aOutputFile ) && aOutputFile.Len() ) - { - aJStr = pEnv->NewStringUTF( ByteString( aLocalFile.GetBuffer(), RTL_TEXTENCODING_UTF8 ).GetBuffer() ); - aArgs = static_cast<jobjectArray>(pEnv->NewObjectArray( 2, pEnv->FindClass( "java/lang/String" ), aJStr )); - aJStr = pEnv->NewStringUTF( ByteString( aOutputFile.GetBuffer(), RTL_TEXTENCODING_UTF8 ).GetBuffer() ); - pEnv->SetObjectArrayElement( aArgs, 1, aJStr ); - pEnv->CallStaticVoidMethod( aClass, aMId, aArgs ); - - Graphic aGraphic; - SvStream* pIStm = ::utl::UcbStreamHelper::CreateStream( aOutputURL, STREAM_READ ); - - if( pIStm ) - { - GraphicConverter::Import( *pIStm, aGraphic ); - delete pIStm; - } - - Reference< XDrawPagesSupplier > xDrawPagesSupplier( mxDstDoc, UNO_QUERY ); - - if( xDrawPagesSupplier.is() && ( aGraphic.GetType() != GRAPHIC_NONE ) ) - { - Reference< XDrawPages > xDrawPages( xDrawPagesSupplier->getDrawPages() ); - - if( xDrawPages.is() && xDrawPages->getCount() ) - { - Reference< XDrawPage > xDrawPage; - - if( xDrawPages->getByIndex( 0 ) >>= xDrawPage ) - { - Reference< XShapes > xShapes( xDrawPage, UNO_QUERY ); - Reference< XPropertySet> xPagePropSet( xDrawPage, UNO_QUERY ); - Reference< XShape > xShape( Reference< XMultiServiceFactory >( mxDstDoc, UNO_QUERY )->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.drawing.GraphicObjectShape" ) ) ), UNO_QUERY ); - - if( xPagePropSet.is() && xShapes.is() && xShape.is() ) - { - Reference< XPropertySet > xPropSet( xShape, UNO_QUERY ); - sal_Int32 nPageWidth = 0, nPageHeight = 0; - - xPagePropSet->getPropertyValue( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "Width" ) ) ) >>= nPageWidth; - xPagePropSet->getPropertyValue( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "Height" ) ) ) >>= nPageHeight; - - if( xPropSet.is() && nPageWidth && nPageHeight ) - { - xShapes->add( xShape ); - - ::com::sun::star::awt::Point aPos; - ::com::sun::star::awt::Size aSize; - GraphicObject aGraphObj( aGraphic ); - String aGraphURL( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.GraphicObject:" ) ); - Any aValue; - Size aGraphicSize; - const MapMode aTargetMapMode( MAP_100TH_MM ); - - if( aGraphObj.GetPrefMapMode().GetMapUnit() == MAP_PIXEL ) - aGraphicSize = Application::GetDefaultDevice()->PixelToLogic( aGraphObj.GetPrefSize(), aTargetMapMode ); - else - aGraphicSize = OutputDevice::LogicToLogic( aGraphObj.GetPrefSize(), aGraphObj.GetPrefMapMode(), aTargetMapMode ); - - aGraphURL += String( aGraphObj.GetUniqueID(), RTL_TEXTENCODING_ASCII_US ); - aValue <<= rtl::OUString( aGraphURL ); - xPropSet->setPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GraphicURL" ) ), aValue ); - - aPos.X = ( nPageWidth - aGraphicSize.Width() ) >> 1; - aPos.Y = ( nPageHeight - aGraphicSize.Height() ) >> 1; - - aSize.Width = aGraphicSize.Width(); - aSize.Height = aGraphicSize.Height(); - - xShape->setPosition( aPos ); - xShape->setSize( aSize ); - - bRet = sal_True; - } - } - } - } - } - } - } - } - } - catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) - { - } + pAttribs->Value >>= xInputStream; } + else if( pAttribs->Name.equalsAscii( "StatusIndicator" ) ) + pAttribs->Value >>= xStatus; } - return bRet; + + OSL_ASSERT(xInputStream.is()); + if(!xInputStream.is()) + return sal_False; + + rtl::OUString sXMLImportService ( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.comp.Draw.XMLOasisImporter" ) ); + Reference < XDocumentHandler > xInternalHandler( mxMSF->createInstance( sXMLImportService ), UNO_QUERY ); + + // The XImporter sets up an empty target document for XDocumentHandler to write to.. + uno::Reference < XImporter > xImporter(xInternalHandler, UNO_QUERY); + xImporter->setTargetDocument(mxDstDoc); + + SVGReader aReader(mxMSF, xInputStream, xInternalHandler); + return aReader.parseAndConvert(); } diff --git a/filter/source/svg/svgreader.cxx b/filter/source/svg/svgreader.cxx new file mode 100644 index 000000000000..d24ab7136477 --- /dev/null +++ b/filter/source/svg/svgreader.cxx @@ -0,0 +1,1876 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_filter.hxx" + +#include "svgreader.hxx" +#include <xmloff/attrlist.hxx> +#include "gfxtypes.hxx" +#include "units.hxx" +#include "parserfragments.hxx" +#include "tokenmap.hxx" +#include "b2dellipse.hxx" + +#include <rtl/math.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <basegfx/vector/b2enums.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/xml/dom/XDocumentBuilder.hpp> +#include <com/sun/star/xml/dom/NodeType.hpp> + +#include <boost/bind.hpp> +#include <hash_set> +#include <map> +#include <string.h> + +#define USTR(x) rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( x ) ) +#define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:" + +using namespace ::com::sun::star; + +namespace svgi +{ +namespace +{ + +/** visits all children of the specified type with the given functor + */ +template<typename Func> void visitChildren(const Func& rFunc, + const uno::Reference<xml::dom::XElement> xElem, + xml::dom::NodeType eChildType ) +{ + uno::Reference<xml::dom::XNodeList> xChildren( xElem->getChildNodes() ); + const sal_Int32 nNumNodes( xChildren->getLength() ); + for( sal_Int32 i=0; i<nNumNodes; ++i ) + { + if( xChildren->item(i)->getNodeType() == eChildType ) + rFunc( *xChildren->item(i).get() ); + } +} + +/** Visit all elements of the given tree (in-order traversal) + + Given functor is called for every element, and passed the + element's attributes, if any + */ +template<typename Func> void visitElements(Func& rFunc, + const uno::Reference<xml::dom::XElement> xElem) +{ + if( xElem->hasAttributes() ) + rFunc(xElem,xElem->getAttributes()); + else + rFunc(xElem); + + // notify children processing + rFunc.push(); + + // recurse over children + uno::Reference<xml::dom::XNodeList> xChildren( xElem->getChildNodes() ); + const sal_Int32 nNumNodes( xChildren->getLength() ); + for( sal_Int32 i=0; i<nNumNodes; ++i ) + { + if( xChildren->item(i)->getNodeType() == xml::dom::NodeType_ELEMENT_NODE ) + visitElements( rFunc, + uno::Reference<xml::dom::XElement>( + xChildren->item(i), + uno::UNO_QUERY_THROW) ); + } + + // children processing done + 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, + StateMap& rStateMap, + const State& rInitialState, + const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) : + mnCurrStateId(0), + maCurrState(), + maParentStates(), + mrStates(rStatePool), + mrStateMap(rStateMap), + mxDocumentHandler(xDocumentHandler), + maGradientVector(), + maGradientStopVector() + { + maParentStates.push_back(rInitialState); + } + + void operator()( const uno::Reference<xml::dom::XElement>& ) + {} + + void operator()( const uno::Reference<xml::dom::XElement>& xElem, + const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes ) + { + const sal_Int32 nTagId(getTokenId(xElem->getTagName())); + switch (nTagId) + { + case XML_LINEARGRADIENT: + { + const sal_Int32 nNumAttrs( xAttributes->getLength() ); + rtl::OUString sAttributeValue; + 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 ) + { + 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; + maGradientStopVector.push_back(GradientStop()); + maGradientVector.back().maStops.push_back(maGradientStopVector.size()-1); + for( sal_Int32 i=0; i<nNumAttrs; ++i ) + { + parseGradientStop( maGradientStopVector.back(), + maGradientStopVector.size()-1, + getTokenId(xAttributes->item(i)->getNodeName()), + xAttributes->item(i)->getNodeValue() ); + } + break; + } + default: + { + // init state. inherit defaults from parent. + maCurrState = maParentStates.back(); + maCurrState.maTransform.identity(); + + // scan for style info + const sal_Int32 nNumAttrs( xAttributes->getLength() ); + rtl::OUString sAttributeValue; + for( sal_Int32 i=0; i<nNumAttrs; ++i ) + { + sAttributeValue = xAttributes->item(i)->getNodeValue(); + const sal_Int32 nTokenId( + getTokenId(xAttributes->item(i)->getNodeName())); + if( XML_STYLE == nTokenId ) + parseStyle(sAttributeValue); + else + parseAttribute(nTokenId, + sAttributeValue); + } + + // all attributes parsed, can calc total CTM now + basegfx::B2DHomMatrix aLocalTransform; + if( !maCurrState.maViewBox.isEmpty() && + maCurrState.maViewBox.getWidth() != 0.0 && + maCurrState.maViewBox.getHeight() != 0.0 ) + { + // transform aViewBox into viewport, such that they + // coincide + aLocalTransform.translate(-maCurrState.maViewBox.getMinX(), + -maCurrState.maViewBox.getMinY()); + aLocalTransform.scale(maCurrState.maViewport.getWidth()/maCurrState.maViewBox.getWidth(), + maCurrState.maViewport.getHeight()/maCurrState.maViewBox.getHeight()); + } + maCurrState.maCTM = maCurrState.maCTM*maCurrState.maTransform*aLocalTransform; + + OSL_TRACE("annotateStyle - CTM is: %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)); + + // if necessary, serialize to automatic-style section + writeStyle(xElem,nTagId); + } + } + } + + rtl::OUString getStyleName( const char* sPrefix, sal_Int32 nId ) + { + 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(1,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 + return sal::static_int_cast<sal_Int8>( + basegfx::fround(val*255.0)); + } + + rtl::OUString getOdfColor( const ARGBColor& rColor ) + { + // TODO(Q3): duplicated from pdfimport + rtl::OUStringBuffer aBuf( 7 ); + const sal_uInt8 nRed ( toByteColor(rColor.r) ); + const sal_uInt8 nGreen( toByteColor(rColor.g) ); + const sal_uInt8 nBlue ( toByteColor(rColor.b) ); + aBuf.append( sal_Unicode('#') ); + if( nRed < 10 ) + aBuf.append( sal_Unicode('0') ); + aBuf.append( sal_Int32(nRed), 16 ); + if( nGreen < 10 ) + aBuf.append( sal_Unicode('0') ); + aBuf.append( sal_Int32(nGreen), 16 ); + if( nBlue < 10 ) + aBuf.append( sal_Unicode('0') ); + aBuf.append( sal_Int32(nBlue), 16 ); + + // TODO(F3): respect alpha transparency (polygons etc.) + OSL_ASSERT(rColor.a == 1.0); + + return aBuf.makeStringAndClear(); + } + + bool writeStyle(State& rState, const sal_Int32 nTagId) + { + rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() ); + uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() ); + + std::pair<StatePool::iterator, + bool> aRes = mrStates.insert(rState); + if( !aRes.second ) + return false; // not written + + ++mnCurrStateId; + + // mnStyleId does not take part in hashing/comparison + const_cast<State&>(*aRes.first).mnStyleId = mnCurrStateId; + 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(); + // 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-width"), USTR("true")); + xAttrs->AddAttribute( USTR( "draw:textarea-horizontal-align"), USTR("left")); + //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( pt2mm(aVec.getLength()) )+USTR("mm")); + } + if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_MITER ) + xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("miter")); + else if( maCurrState.meLineJoin == basegfx::B2DLINEJOIN_ROUND ) + xAttrs->AddAttribute( USTR( "draw:stroke-linejoin"), USTR("round")); + else if( maCurrState.meLineJoin == basegfx::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"), + xUnoAttrs ); + mxDocumentHandler->endElement( USTR("style:graphic-properties") ); + mxDocumentHandler->endElement( USTR("style:style") ); + + return true; // newly written + } + + void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, const sal_Int32 nTagId) + { + sal_Int32 nEmulatedStyleId=0; + if( maCurrState.maDashArray.size() && + maCurrState.meStrokeType != NONE ) + { + // ODF dashing is severly borked - generate filled shape + // instead (further down the road - here, we simply + // emulate a filled style with the next id) + + // move all stroke attribs to fill, Clear stroking + State aEmulatedStrokeState( maCurrState ); + aEmulatedStrokeState.meFillType = maCurrState.meStrokeType; + aEmulatedStrokeState.mnFillOpacity = maCurrState.mnStrokeOpacity; + aEmulatedStrokeState.maFillColor = maCurrState.maStrokeColor; + aEmulatedStrokeState.maFillGradient = maCurrState.maStrokeGradient; + aEmulatedStrokeState.meFillRule = EVEN_ODD; + aEmulatedStrokeState.meStrokeType = NONE; + + if( writeStyle(aEmulatedStrokeState, nTagId) ) + nEmulatedStyleId = mnCurrStateId; + else + nEmulatedStyleId = mrStates.find(aEmulatedStrokeState)->mnStyleId; + } + + sal_Int32 nStyleId=0; + if( writeStyle(maCurrState, nTagId) ) + nStyleId = mnCurrStateId; + else + nStyleId = mrStates.find(maCurrState)->mnStyleId; + + xElem->setAttribute(USTR("internal-style-ref"), + rtl::OUString::valueOf( + nStyleId) + +USTR("$") + +rtl::OUString::valueOf( + nEmulatedStyleId)); + } + + void push() + { + maParentStates.push_back(maCurrState); + } + + void pop() + { + maParentStates.pop_back(); + } + + 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: + io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue,maCurrState,'h'); + break; + case XML_X2: + io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue,maCurrState,'h'); + break; + case XML_Y1: + io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue,maCurrState,'v'); + break; + case XML_Y2: + io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue,maCurrState,'v'); + 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 + io_rCurrGradient.mbBoundingBoxUnits = false; + break; + 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,maCurrState,'h'); + break; + case XML_CY: + io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue,maCurrState,'v'); + break; + case XML_FX: + io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue,maCurrState,'h'); + break; + case XML_FY: + io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue,maCurrState,'v'); + break; + case XML_R: + io_rCurrGradient.maCoords.radial.mfR = convLength(sValue,maCurrState,'r'); + 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 + io_rCurrGradient.mbBoundingBoxUnits = false; + break; + default: + break; + } + } + + 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: + maStopIdMap.insert(std::make_pair(sValue,nStopNumber)); + break; + case XML_OFFSET: + io_rGradientStop.mnStopPosition = sValue.toDouble(); + break; + case XML_STYLE: + parseStyle( sValue ); + break; + default: + break; + } + } + + void parseAttribute( const sal_Int32 nTokenId, + const rtl::OUString& sValue ) + { + rtl::OString aValueUtf8( sValue.getStr(), + sValue.getLength(), + RTL_TEXTENCODING_UTF8 ); + switch(nTokenId) + { + case XML_WIDTH: + { + const double fViewPortWidth( + convLength(sValue,maCurrState,'h')); + + maCurrState.maViewport.expand( + basegfx::B2DTuple(fViewPortWidth,0.0)); + break; + } + case XML_HEIGHT: + { + const double fViewPortHeight( + convLength(sValue,maCurrState,'v')); + + maCurrState.maViewport.expand( + basegfx::B2DTuple(0.0,fViewPortHeight)); + break; + } + case XML_VIEWBOX: + { + // TODO(F1): preserveAspectRatio + parseViewBox( + aValueUtf8, + maCurrState.maViewBox); + break; + } + case XML_FILL_RULE: + { + if( aValueUtf8 == "evenodd" ) + maCurrState.meFillRule = EVEN_ODD; + else if( aValueUtf8 == "nonzero" ) + maCurrState.meFillRule = NON_ZERO; + else if( aValueUtf8 == "inherit" ) + 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" ) + maCurrState.mnStrokeWidth = maParentStates.back().mnStrokeWidth; + else + maCurrState.mnStrokeWidth = convLength(sValue,maCurrState,'r'); + break; + } + case XML_STROKE_LINECAP: + { + if( aValueUtf8 == "butt" ) + maCurrState.meLineCap = BUTT; + else if( aValueUtf8 == "round" ) + maCurrState.meLineCap = ROUND; + else if( aValueUtf8 == "square" ) + maCurrState.meLineCap = RECT; + else if( aValueUtf8 == "inherit" ) + maCurrState.meLineCap = maParentStates.back().meLineCap; + break; + } + case XML_STROKE_LINEJOIN: + { + if( aValueUtf8 == "miter" ) + maCurrState.meLineJoin = basegfx::B2DLINEJOIN_MITER; + else if( aValueUtf8 == "round" ) + maCurrState.meLineJoin = basegfx::B2DLINEJOIN_ROUND; + else if( aValueUtf8 == "bevel" ) + maCurrState.meLineJoin = basegfx::B2DLINEJOIN_BEVEL; + else if( aValueUtf8 == "inherit" ) + maCurrState.meLineJoin = maParentStates.back().meLineJoin; + break; + } + case XML_STROKE_MITERLIMIT: + { + if( aValueUtf8 == "inherit" ) + maCurrState.mnMiterLimit = maParentStates.back().mnMiterLimit; + else + maCurrState.mnMiterLimit = aValueUtf8.toDouble(); + break; + } + case XML_STROKE_DASHOFFSET: + { + if( aValueUtf8 == "inherit" ) + maCurrState.mnDashOffset = maParentStates.back().mnDashOffset; + else + maCurrState.mnDashOffset = convLength(sValue,maCurrState,'r'); + break; + } + case XML_STROKE_DASHARRAY: + { + if( aValueUtf8 == "none" ) + maCurrState.maDashArray.clear(); + else if( aValueUtf8 == "inherit" ) + maCurrState.maDashArray = maParentStates.back().maDashArray; + else + parseDashArray(aValueUtf8.getStr(), + 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( sValue, + aValueUtf8.getStr(), + maCurrState.meFillType, + maCurrState.maFillColor, + maCurrState.maFillGradient, + rParent.meFillType, + rParent.maFillColor, + rParent.maFillGradient ); + break; + } + case XML_STROKE: + { + const State& rParent( maParentStates.back() ); + parsePaint( sValue, + aValueUtf8.getStr(), + maCurrState.meStrokeType, + maCurrState.maStrokeColor, + maCurrState.maStrokeGradient, + rParent.meStrokeType, + rParent.maStrokeColor, + rParent.maStrokeGradient ); + break; + } + case XML_TRANSFORM: + { + basegfx::B2DHomMatrix aTransform; + parseTransform(aValueUtf8.getStr(),aTransform); + maCurrState.maTransform = maCurrState.maTransform*aTransform; + break; + } + case XML_FONT_FAMILY: + maCurrState.maFontFamily=sValue; + break; + case XML_FONT_SIZE: + maCurrState.mnFontSize=convLength(sValue,maCurrState,'v'); + break; + case XML_FONT_STYLE: + maCurrState.meFontStyle=STYLE_ITALIC; // TODO: sValue.toStyleId(); + break; + case XML_FONT_WEIGHT: + maCurrState.mnFontWeight=sValue.toDouble(); + break; + case XML_FONT_VARIANT: + maCurrState.meFontVariant=VARIANT_SMALLCAPS; // TODO: sValue.toDouble(); + break; + case XML_STOP_COLOR: + if( maGradientVector.empty() || + maGradientVector.back().maStops.empty() ) + break; + parseColor( aValueUtf8, + maGradientStopVector[ + maGradientVector.back().maStops.back()].maStopColor ); + break; + case XML_STOP_OPACITY: + 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)); + break; + } + } + + void parseStyle( const rtl::OUString& sValue ) + { + // split individual style attributes + sal_Int32 nIndex=0, nDummyIndex=0; + rtl::OUString aCurrToken; + do + { + aCurrToken=sValue.getToken(0,';',nIndex); + + if( aCurrToken.getLength() ) + { + // split attrib & value + nDummyIndex=0; + rtl::OUString aCurrAttrib( + aCurrToken.getToken(0,':',nDummyIndex).trim()); + OSL_ASSERT(nDummyIndex!=-1); + nDummyIndex=0; + rtl::OUString aCurrValue( + aCurrToken.getToken(1,':',nDummyIndex).trim()); + OSL_ASSERT(nDummyIndex==-1); + + // recurse into normal attribute parsing + parseAttribute( getTokenId(aCurrAttrib), + aCurrValue ); + } + } + while( nIndex != -1 ); + } + + void parsePaint( const rtl::OUString& rValue, + const char* sValue, + PaintType& rType, + ARGBColor& rColor, + Gradient& rGradient, + const PaintType& rInheritType, + const ARGBColor& rInheritColor, + const Gradient& rInheritGradient ) + { + if( strcmp(sValue,"none") == 0 ) + rType = NONE; + else if( strcmp(sValue,"currentColor") == 0 ) + { + rType = SOLID; + rColor = maCurrState.maCurrentColor; + } + else if( strcmp(sValue,"inherit") == 0) + { + rType = rInheritType; + rColor = rInheritColor; + rGradient = rInheritGradient; + } + 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 + { + rType = SOLID; + parseColor(sValue,rColor); + } + } + + 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 +static void annotateStyles( StatePool& rStatePool, + StateMap& rStateMap, + const State& rInitialState, + const uno::Reference<xml::dom::XElement> xElem, + const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl ) +{ + AnnotatingVisitor aVisitor(rStatePool,rStateMap,rInitialState,xDocHdl); + visitElements(aVisitor, xElem); +} + +struct ShapeWritingVisitor +{ + ShapeWritingVisitor(StatePool& /*rStatePool*/, + StateMap& rStateMap, + const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) : + mrStateMap(rStateMap), + mxDocumentHandler(xDocumentHandler), + mnShapeNum(0) + {} + + void operator()( const uno::Reference<xml::dom::XElement>& ) + { + } + + void operator()( const uno::Reference<xml::dom::XElement>& xElem, + const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes ) + { + rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() ); + uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() ); + + sal_Int32 nDummyIndex(0); + rtl::OUString sStyleId( + xElem->getAttribute( + USTR("internal-style-ref")).getToken( + 0,'$',nDummyIndex)); + StateMap::iterator pOrigState=mrStateMap.find( + sStyleId.toInt32()); + + if( pOrigState == mrStateMap.end() ) + return; // non-exportable element, e.g. linearGradient + + maCurrState = pOrigState->second; + + 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,maCurrState,'h'); + break; + case XML_X2: + x2 = convLength(sAttributeValue,maCurrState,'h'); + break; + case XML_Y1: + y1 = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_Y2: + y2 = convLength(sAttributeValue,maCurrState,'v'); + break; + default: + // skip + break; + } + } + + rtl::OUString sLinePath = USTR("M")+rtl::OUString::valueOf(x1)+USTR(",") + +rtl::OUString::valueOf(y1)+USTR("L")+rtl::OUString::valueOf(x2)+USTR(",") + +rtl::OUString::valueOf(y2); + basegfx::B2DPolyPolygon aPoly; + basegfx::tools::importFromSvgD(aPoly, sLinePath); + + writePathShape(xAttrs, + xUnoAttrs, + xElem, + sStyleId, + 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 || maCurrState.meFillType != NONE ) + aPoly.setClosed(true); + + writePathShape(xAttrs, + xUnoAttrs, + xElem, + sStyleId, + 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,maCurrState,'h'); + break; + case XML_Y: + y = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_WIDTH: + width = convLength(sAttributeValue,maCurrState,'h'); + break; + case XML_HEIGHT: + height = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_RX: + rx = convLength(sAttributeValue,maCurrState,'h'); + bRxSeen=true; + break; + case XML_RY: + ry = convLength(sAttributeValue,maCurrState,'v'); + 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/width, ry/height ); + + writePathShape(xAttrs, + xUnoAttrs, + xElem, + sStyleId, + 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); + + writePathShape(xAttrs, + xUnoAttrs, + xElem, + sStyleId, + 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,maCurrState,'h'); + break; + case XML_CY: + cy = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_R: + r = convLength(sAttributeValue,maCurrState,'r'); + default: + // skip + break; + } + } + + writeEllipseShape(xAttrs, + xUnoAttrs, + xElem, + sStyleId, + basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r))); + 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,maCurrState,'h'); + break; + case XML_CY: + cy = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_RX: + rx = convLength(sAttributeValue,maCurrState,'h'); + break; + case XML_RY: + ry = convLength(sAttributeValue,maCurrState,'v'); + default: + // skip + break; + } + } + + writeEllipseShape(xAttrs, + xUnoAttrs, + xElem, + sStyleId, + basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry))); + 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,maCurrState,'h'); + break; + case XML_Y: + y = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_WIDTH: + width = convLength(sAttributeValue,maCurrState,'h'); + break; + case XML_HEIGHT: + height = convLength(sAttributeValue,maCurrState,'v'); + 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()) + writeBinaryData(xAttrs, xUnoAttrs, xElem, basegfx::B2DRange(x,y,x+width,y+height), sLinkValue); + 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,maCurrState,'h'); + break; + case XML_Y: + y = convLength(sAttributeValue,maCurrState,'v'); + break; + case XML_WIDTH: + width = convLength(sAttributeValue,maCurrState,'h'); + break; + case XML_HEIGHT: + height = convLength(sAttributeValue,maCurrState,'v'); + break; + default: + // skip + break; + } + } + + // 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(pt2mm(x))+USTR("mm")); + xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(pt2mm(y))+USTR("mm")); + xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("svggraphicstyle")+sStyleId ); + + mxDocumentHandler->startElement(USTR("draw:frame"),xUnoAttrs); + + xAttrs->Clear(); + mxDocumentHandler->startElement(USTR("draw:text-box"),xUnoAttrs); + // TODO: put text style in here + mxDocumentHandler->startElement(USTR("text:p"),xUnoAttrs); + mxDocumentHandler->characters(sText.makeStringAndClear()); + mxDocumentHandler->endElement(USTR("text:p")); + mxDocumentHandler->endElement(USTR("draw:text-box")); + mxDocumentHandler->endElement(USTR("draw:frame")); + break; + } + } + } + + void push() + {} + + void pop() + {} + + void writeBinaryData( rtl::Reference<SvXMLAttributeList>& xAttrs, + const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs, + const uno::Reference<xml::dom::XElement>& /* xElem */, + const basegfx::B2DRange& rShapeBounds, + const std::string& data) + { + xAttrs->Clear(); + xAttrs->AddAttribute( USTR( "svg:x" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinX()))+USTR("mm")); + xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinY()))+USTR("mm")); + xAttrs->AddAttribute( USTR( "svg:width" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getWidth()))+USTR("mm")); + xAttrs->AddAttribute( USTR( "svg:height" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getHeight()))+USTR("mm")); + + mxDocumentHandler->startElement(USTR("draw:frame"),xUnoAttrs); + + xAttrs->Clear(); + mxDocumentHandler->startElement(USTR("draw:image"),xUnoAttrs); + + mxDocumentHandler->startElement(USTR("office:binary-data"),xUnoAttrs); + + mxDocumentHandler->characters(rtl::OUString::createFromAscii(data.c_str())); + + mxDocumentHandler->endElement(USTR("office:binary-data")); + + mxDocumentHandler->endElement(USTR("draw:image")); + + mxDocumentHandler->endElement(USTR("draw:frame")); + } + + + void writeTransformAttribute(const basegfx::B2DHomMatrix rMatrix, rtl::Reference<SvXMLAttributeList>& xAttrs) + { + basegfx::B2DTuple rScale, rTranslate; + double rRotate, rShearX; + ::rtl::OUString sTransformValue; + if (!rMatrix.decompose(rScale, rTranslate, rRotate, rShearX)) + return; + if (rScale.getX() != 1.0 || rScale.getY() != 1.0) + sTransformValue += USTR("scale(")+::rtl::OUString::valueOf(rScale.getX())+USTR(" ") + +::rtl::OUString::valueOf(rScale.getY())+USTR(") "); + if (rTranslate.getX() != 0.0f || rTranslate.getY() != 0.0f) + sTransformValue += USTR("translate(")+::rtl::OUString::valueOf(rTranslate.getX()/100.0f)+USTR("mm ") + +::rtl::OUString::valueOf(rTranslate.getY()/100.0f)+USTR("mm) "); + if (rRotate != 0.0f) + sTransformValue += USTR("rotate(")+::rtl::OUString::valueOf(rRotate)+USTR(") "); + + if (rShearX != 0.0f) + sTransformValue += USTR("skewX(")+::rtl::OUString::valueOf(rShearX)+USTR(") "); + if (!sTransformValue.getLength()) + return; + xAttrs->AddAttribute( USTR("draw:transform"), sTransformValue); + } + + void writeEllipseShape( rtl::Reference<SvXMLAttributeList>& xAttrs, + const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs, + const uno::Reference<xml::dom::XElement>& xElem, + const rtl::OUString& rStyleId, + const basegfx::B2DEllipse& rEllipse) + { + State aState = maCurrState; + rtl::OUString aStyleId(rStyleId); + + xAttrs->Clear(); + + basegfx::B2DPolygon aPoly = basegfx::tools::createPolygonFromEllipse(rEllipse.getB2DEllipseCenter(), + rEllipse.getB2DEllipseRadius().getX(), rEllipse.getB2DEllipseRadius().getY()); + writePathShape(xAttrs, xUnoAttrs, xElem, rStyleId, basegfx::B2DPolyPolygon(aPoly)); + + } + + void writePathShape( rtl::Reference<SvXMLAttributeList>& xAttrs, + const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs, + const uno::Reference<xml::dom::XElement>& xElem, + const rtl::OUString& rStyleId, + const basegfx::B2DPolyPolygon& rPoly ) + { + // we might need to split up polypolygon into multiple path + // shapes (e.g. when emulating line stroking) + std::vector<basegfx::B2DPolyPolygon> aPolys(1,rPoly); + State aState = maCurrState; + rtl::OUString aStyleId(rStyleId); + + xAttrs->Clear(); + + OSL_TRACE("writePath - the CTM is: %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)); + + if( aState.meStrokeType != NONE && aState.maDashArray.size() ) + { + // ODF dashing is severly borked - generate filled polygon instead + aPolys.clear(); + for( sal_uInt32 i=0; i<rPoly.count(); ++i ) + { + aPolys.push_back( + basegfx::tools::createAreaGeometryForPolygon( + rPoly.getB2DPolygon(i), + aState.mnStrokeWidth/2.0, + aState.meLineJoin)); + // TODO(F2): line ends + } + + sal_Int32 nDummyIndex(0); + aStyleId = xElem->getAttribute( + USTR("internal-style-ref")).getToken(1,'$',nDummyIndex); + StateMap::iterator pAlternateState=mrStateMap.find(aStyleId.toInt32()); + OSL_ASSERT(pAlternateState != mrStateMap.end()); + aState = pAlternateState->second; + OSL_ENSURE( pAlternateState == mrStateMap.end(), + "Doh - where's my alternate style entry?!" ); + } + + // TODO(F2): separate out shear, rotate etc. + // apply transformation to polygon, to keep draw + // import in 100th mm + std::for_each(aPolys.begin(),aPolys.end(), + boost::bind(&basegfx::B2DPolyPolygon::transform, + _1,boost::cref(aState.maCTM))); + + for( sal_uInt32 i=0; i<aPolys.size(); ++i ) + { + const basegfx::B2DRange aBounds( + aPolys[i].areControlPointsUsed() ? + basegfx::tools::getRange( + basegfx::tools::adaptiveSubdivideByAngle(aPolys[i])) : + basegfx::tools::getRange(aPolys[i])); + fillShapeProperties(xAttrs, + xElem, + aBounds, + USTR("svggraphicstyle")+aStyleId); + + // force path coordinates to 100th millimeter, after + // putting polygon data at origin (odf viewbox + // calculations largely untested codepaths, as OOo always + // writes "0 0 w h" viewboxes) + basegfx::B2DHomMatrix aNormalize; + aNormalize.translate(-aBounds.getMinX(),-aBounds.getMinY()); + aNormalize.scale(2540.0/72.0,2540.0/72.0); + aPolys[i].transform(aNormalize); + + xAttrs->AddAttribute( USTR( "svg:d" ), basegfx::tools::exportToSvgD( + aPolys[i], + false, // no relative coords. causes rounding errors + false )); // no quad bezier detection. crashes older versions. + mxDocumentHandler->startElement(USTR("draw:path"), + xUnoAttrs); + mxDocumentHandler->endElement(USTR("draw:path")); + } + } + + void fillShapeProperties( rtl::Reference<SvXMLAttributeList>& xAttrs, + const uno::Reference<xml::dom::XElement>& /* xElem */, + const basegfx::B2DRange& rShapeBounds, + const rtl::OUString& rStyleName ) + { + xAttrs->AddAttribute( USTR( "draw:z-index" ), rtl::OUString::valueOf( mnShapeNum++ )); + xAttrs->AddAttribute( USTR( "draw:style-name" ), rStyleName); + xAttrs->AddAttribute( USTR( "svg:width" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getWidth()))+USTR("mm")); + xAttrs->AddAttribute( USTR( "svg:height" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getHeight()))+USTR("mm")); + + // OOo expects the viewbox to be in 100th of mm + xAttrs->AddAttribute( USTR( "svg:viewBox" ), + USTR("0 0 ") + + rtl::OUString::valueOf( + basegfx::fround(pt100thmm(rShapeBounds.getWidth())) ) + + USTR(" ") + + rtl::OUString::valueOf( + basegfx::fround(pt100thmm(rShapeBounds.getHeight())) )); + + // TODO(F1): decompose transformation in calling code, and use + // transform attribute here + // writeTranslate(maCurrState.maCTM, xAttrs); + xAttrs->AddAttribute( USTR( "svg:x" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinX()))+USTR("mm")); + xAttrs->AddAttribute( USTR( "svg:y" ), rtl::OUString::valueOf(pt2mm(rShapeBounds.getMinY()))+USTR("mm")); + } + + State maCurrState; + StateMap& mrStateMap; + uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler; + sal_Int32 mnShapeNum; +}; + +/// Write out shapes from DOM tree +static void writeShapes( StatePool& rStatePool, + StateMap& rStateMap, + const uno::Reference<xml::dom::XElement> xElem, + const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl ) +{ + ShapeWritingVisitor aVisitor(rStatePool,rStateMap,xDocHdl); + visitElements(aVisitor, xElem); +} + +#ifdef VERBOSE +struct DumpingVisitor +{ + void operator()( const uno::Reference<xml::dom::XElement>& xElem ) + { + OSL_TRACE("name: %s", + rtl::OUStringToOString( + xElem->getTagName(), + RTL_TEXTENCODING_UTF8 ).getStr()); + } + + void operator()( const uno::Reference<xml::dom::XElement>& xElem, + const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes ) + { + OSL_TRACE("name: %s", + rtl::OUStringToOString( + xElem->getTagName(), + RTL_TEXTENCODING_UTF8 ).getStr()); + const sal_Int32 nNumAttrs( xAttributes->getLength() ); + for( sal_Int32 i=0; i<nNumAttrs; ++i ) + { + OSL_TRACE(" %s=%s", + rtl::OUStringToOString( + xAttributes->item(i)->getNodeName(), + RTL_TEXTENCODING_UTF8 ).getStr(), + rtl::OUStringToOString( + xAttributes->item(i)->getNodeValue(), + RTL_TEXTENCODING_UTF8 ).getStr()); + } + } + + void push() {} + void pop() {} +}; + +static void dumpTree( const uno::Reference<xml::dom::XElement> xElem ) +{ + DumpingVisitor aVisitor; + visitElements(aVisitor, xElem); +} +#endif + +} // namespace + + +SVGReader::SVGReader(const uno::Reference<lang::XMultiServiceFactory>& xServiceFactory, + const uno::Reference<io::XInputStream>& xInputStream, + const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) : + m_xServiceFactory( xServiceFactory ), + m_xInputStream( xInputStream ), + m_xDocumentHandler( xDocumentHandler ) +{ +} + +sal_Bool SVGReader::parseAndConvert() +{ + uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder( + m_xServiceFactory->createInstance( + rtl::OUString::createFromAscii("com.sun.star.xml.dom.DocumentBuilder")), uno::UNO_QUERY ); + + uno::Reference<xml::dom::XDocument> xDom( + xDomBuilder->parse(m_xInputStream), + uno::UNO_QUERY_THROW ); + + uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(), + uno::UNO_QUERY_THROW ); + + // the root state for svg document + State aInitialState; + + ///////////////////////////////////////////////////////////////// + // doc boilerplate + ///////////////////////////////////////////////////////////////// + + m_xDocumentHandler->startDocument(); + + // 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")); + + double fViewPortWidth( pt2mm(convLength(xDocElem->getAttribute(USTR("width")),aInitialState,'h')) ); + double fViewPortHeight( pt2mm(convLength(xDocElem->getAttribute(USTR("height")),aInitialState,'v')) ); + + // document prolog + rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() ); + uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() ); + + xAttrs->AddAttribute( USTR( "xmlns:office" ), USTR( OASIS_STR "office:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:style" ), USTR( OASIS_STR "style:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:text" ), USTR( OASIS_STR "text:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:svg" ), USTR( OASIS_STR "svg-compatible:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:table" ), USTR( OASIS_STR "table:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:draw" ), USTR( OASIS_STR "drawing:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:fo" ), USTR( OASIS_STR "xsl-fo-compatible:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:xlink" ), USTR( "http://www.w3.org/1999/xlink" )); + xAttrs->AddAttribute( USTR( "xmlns:dc" ), USTR( "http://purl.org/dc/elements/1.1/" )); + xAttrs->AddAttribute( USTR( "xmlns:number" ), USTR( OASIS_STR "datastyle:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:presentation" ), USTR( OASIS_STR "presentation:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:math" ), USTR( "http://www.w3.org/1998/Math/MathML" )); + xAttrs->AddAttribute( USTR( "xmlns:form" ), USTR( OASIS_STR "form:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:script" ), USTR( OASIS_STR "script:1.0" )); + xAttrs->AddAttribute( USTR( "xmlns:dom" ), USTR( "http://www.w3.org/2001/xml-events" )); + xAttrs->AddAttribute( USTR( "xmlns:xforms" ), USTR( "http://www.w3.org/2002/xforms" )); + xAttrs->AddAttribute( USTR( "xmlns:xsd" ), USTR( "http://www.w3.org/2001/XMLSchema" )); + xAttrs->AddAttribute( USTR( "xmlns:xsi" ), USTR( "http://www.w3.org/2001/XMLSchema-instance" )); + xAttrs->AddAttribute( USTR( "office:version" ), USTR( "1.0" )); + xAttrs->AddAttribute( USTR( "office:mimetype" ), USTR( "application/vnd.oasis.opendocument.graphics" )); + + m_xDocumentHandler->startElement( USTR("office:document"), xUnoAttrs ); + + xAttrs->Clear(); + + m_xDocumentHandler->startElement( USTR("office:settings"), xUnoAttrs); + + xAttrs->AddAttribute( USTR( "config:name" ), USTR( "ooo:view-settings" )); + m_xDocumentHandler->startElement( USTR("config:config-item-set"), xUnoAttrs); + + xAttrs->Clear(); + + xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaTop" )); + xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" )); + m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs); + + m_xDocumentHandler->characters( USTR( "0" )); + + m_xDocumentHandler->endElement( USTR( "config:config-item" )); + + xAttrs->Clear(); + + xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaLeft" )); + xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" )); + m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs); + + m_xDocumentHandler->characters( USTR( "0" )); + + m_xDocumentHandler->endElement( USTR( "config:config-item" )); + + xAttrs->Clear(); + + xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaWidth" )); + xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" )); + m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs); + + sal_Int64 iWidth = sal_Int64(fViewPortWidth); + m_xDocumentHandler->characters( ::rtl::OUString::valueOf(iWidth) ); + + m_xDocumentHandler->endElement( USTR( "config:config-item" )); + + xAttrs->Clear(); + + xAttrs->AddAttribute( USTR( "config:name" ), USTR( "VisibleAreaHeight" )); + xAttrs->AddAttribute( USTR( "config:type" ), USTR( "int" )); + m_xDocumentHandler->startElement( USTR( "config:config-item" ), xUnoAttrs); + + sal_Int64 iHeight = sal_Int64(fViewPortHeight); + m_xDocumentHandler->characters( ::rtl::OUString::valueOf(iHeight) ); + + m_xDocumentHandler->endElement( USTR( "config:config-item" )); + + m_xDocumentHandler->endElement( USTR( "config:config-item-set" )); + + m_xDocumentHandler->endElement( USTR( "office:settings" )); + + xAttrs->Clear(); + + m_xDocumentHandler->startElement( USTR("office:automatic-styles"), + xUnoAttrs ); + + xAttrs->AddAttribute( USTR( "style:name" ), USTR("pagelayout1")); + m_xDocumentHandler->startElement( USTR("style:page-layout"), + xUnoAttrs ); + // TODO(Q3): this is super-ugly. In-place container come to mind. + xAttrs->Clear(); + + // make page viewport-width times viewport-height mm large - add + // 5% border at every side + xAttrs->AddAttribute( USTR( "fo:margin-top" ), USTR("0mm")); + xAttrs->AddAttribute( USTR( "fo:margin-bottom" ), USTR("0mm")); + xAttrs->AddAttribute( USTR( "fo:margin-left" ), USTR("0mm")); + xAttrs->AddAttribute( USTR( "fo:margin-right" ), USTR("0mm")); + xAttrs->AddAttribute( USTR( "fo:page-width" ), rtl::OUString::valueOf(fViewPortWidth)+USTR("mm")); + xAttrs->AddAttribute( USTR( "fo:page-height" ), rtl::OUString::valueOf(fViewPortHeight)+USTR("mm")); + xAttrs->AddAttribute( USTR( "style:print-orientation" ), + fViewPortWidth > fViewPortHeight ? + USTR("landscape") : + USTR("portrait")); + m_xDocumentHandler->startElement( USTR("style:page-layout-properties"), + xUnoAttrs ); + m_xDocumentHandler->endElement( USTR("style:page-layout-properties") ); + m_xDocumentHandler->endElement( USTR("style:page-layout") ); + + xAttrs->Clear(); + xAttrs->AddAttribute( USTR( "style:name" ), USTR("pagestyle1")); + xAttrs->AddAttribute( USTR( "style:family" ), USTR("drawing-page")); + m_xDocumentHandler->startElement( USTR("style:style"), + xUnoAttrs ); + + xAttrs->Clear(); + xAttrs->AddAttribute( USTR( "draw:background-size" ), USTR("border")); + xAttrs->AddAttribute( USTR( "draw:fill" ), USTR("none")); + m_xDocumentHandler->startElement( USTR("style:drawing-page-properties"), + xUnoAttrs ); + m_xDocumentHandler->endElement( USTR("style:drawing-page-properties") ); + m_xDocumentHandler->endElement( USTR("style:style") ); + + StatePool aStatePool; + StateMap aStateMap; + annotateStyles(aStatePool,aStateMap,aInitialState, + xDocElem,m_xDocumentHandler); + +#ifdef VERBOSE + dumpTree(xDocElem); +#endif + + m_xDocumentHandler->endElement( USTR("office:automatic-styles") ); + + //////////////////////////////////////////////////////////////////// + + xAttrs->Clear(); + m_xDocumentHandler->startElement( USTR("office:styles"), + xUnoAttrs); + m_xDocumentHandler->endElement( USTR("office:styles") ); + + //////////////////////////////////////////////////////////////////// + + m_xDocumentHandler->startElement( USTR("office:master-styles"), + xUnoAttrs ); + xAttrs->Clear(); + xAttrs->AddAttribute( USTR( "style:name" ), USTR("Default")); + xAttrs->AddAttribute( USTR( "style:page-layout-name" ), USTR("pagelayout1")); + xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("pagestyle1")); + m_xDocumentHandler->startElement( USTR("style:master-page"), + xUnoAttrs ); + m_xDocumentHandler->endElement( USTR("style:master-page") ); + + m_xDocumentHandler->endElement( USTR("office:master-styles") ); + + //////////////////////////////////////////////////////////////////// + + xAttrs->Clear(); + m_xDocumentHandler->startElement( USTR("office:body"), + xUnoAttrs ); + m_xDocumentHandler->startElement( USTR("office:drawing"), + xUnoAttrs ); + + xAttrs->Clear(); + xAttrs->AddAttribute( USTR( "draw:master-page-name" ), USTR("Default")); + xAttrs->AddAttribute( USTR( "draw:style-name" ), USTR("pagestyle1")); + m_xDocumentHandler->startElement(USTR("draw:page"), + xUnoAttrs); + + // write out all shapes + writeShapes(aStatePool, + aStateMap, + xDocElem, + m_xDocumentHandler); + + m_xDocumentHandler->endElement( USTR("draw:page") ); + m_xDocumentHandler->endElement( USTR("office:drawing") ); + m_xDocumentHandler->endElement( USTR("office:body") ); + m_xDocumentHandler->endElement( USTR("office:document") ); + m_xDocumentHandler->endDocument(); + + return sal_True; +} + +} // namespace svgi diff --git a/filter/source/svg/svgreader.hxx b/filter/source/svg/svgreader.hxx new file mode 100644 index 000000000000..d4b57d31868d --- /dev/null +++ b/filter/source/svg/svgreader.hxx @@ -0,0 +1,43 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#ifndef INCLUDED_SVGREADER_HXX +#define INCLUDED_SVGREADER_HXX + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/io/XInputStream.hpp> + +namespace svgi +{ + +class SVGReader +{ + const ::com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > m_xServiceFactory; + const ::com::sun::star::uno::Reference< com::sun::star::io::XInputStream > m_xInputStream; + const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XDocumentHandler > m_xDocumentHandler; + +public: + SVGReader( const com::sun::star::uno::Reference<com::sun::star::lang::XMultiServiceFactory>& xServiceFactory, + const com::sun::star::uno::Reference< com::sun::star::io::XInputStream >& xInputStream, + const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XDocumentHandler >& xDocumentHandler ); + + sal_Bool parseAndConvert(); +}; + +} // namespace svgi + +#endif diff --git a/filter/source/svg/test/makefile.mk b/filter/source/svg/test/makefile.mk new file mode 100644 index 000000000000..d51fdda07e68 --- /dev/null +++ b/filter/source/svg/test/makefile.mk @@ -0,0 +1,114 @@ +#************************************************************************* +# +# 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 3. +# +#************************************************************************* + +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) \ + $(SVTOOLLIB) \ + $(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 \ + anarchist2.svg \ + Nested.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 --git a/filter/source/svg/test/odfserializer.cxx b/filter/source/svg/test/odfserializer.cxx new file mode 100644 index 000000000000..2a8958d02c31 --- /dev/null +++ b/filter/source/svg/test/odfserializer.cxx @@ -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 3. + * + ************************************************************************/ + +// 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 --git a/filter/source/svg/test/odfserializer.hxx b/filter/source/svg/test/odfserializer.hxx new file mode 100644 index 000000000000..d9ef2a34cc14 --- /dev/null +++ b/filter/source/svg/test/odfserializer.hxx @@ -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 3. + * + ************************************************************************/ + +#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 --git a/filter/source/svg/test/parsertest.cxx b/filter/source/svg/test/parsertest.cxx new file mode 100644 index 000000000000..04695a54f02c --- /dev/null +++ b/filter/source/svg/test/parsertest.cxx @@ -0,0 +1,209 @@ +/************************************************************************* + * + * 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 31. + * + ************************************************************************/ + +// 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 --git a/filter/source/svg/test/svg2odf.cxx b/filter/source/svg/test/svg2odf.cxx new file mode 100644 index 000000000000..e2235fc4d5ae --- /dev/null +++ b/filter/source/svg/test/svg2odf.cxx @@ -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 3. + * + ************************************************************************/ + +// 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; +} diff --git a/filter/source/svg/tokenmap.cxx b/filter/source/svg/tokenmap.cxx new file mode 100644 index 000000000000..18a1de42031e --- /dev/null +++ b/filter/source/svg/tokenmap.cxx @@ -0,0 +1,62 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_filter.hxx" + +#include "tokenmap.hxx" +#include <string.h> + +namespace svgi +{ + +#include "tokens.cxx" + +sal_Int32 getTokenId( const char* sIdent, sal_Int32 nLen ) +{ + const struct xmltoken* t = Perfect_Hash::in_word_set( sIdent, nLen ); + if( t ) + return t->nToken; + else + return XML_TOKEN_INVALID; +} + +sal_Int32 getTokenId( const rtl::OUString& sIdent ) +{ + rtl::OString aUTF8( sIdent.getStr(), + sIdent.getLength(), + RTL_TEXTENCODING_UTF8 ); + return getTokenId( aUTF8.getStr(), aUTF8.getLength() ); +} + +const char* getTokenName( sal_Int32 nTokenId ) +{ + if( nTokenId >= XML_TOKEN_COUNT ) + return NULL; + + const xmltoken* pCurr=wordlist; + const xmltoken* pEnd=wordlist+sizeof(wordlist)/sizeof(*wordlist); + while( pCurr != pEnd ) + { + if(pCurr->nToken == nTokenId) + return pCurr->name; + ++pCurr; + } + + return NULL; +} + +} // namespace svgi diff --git a/filter/source/svg/tokenmap.hxx b/filter/source/svg/tokenmap.hxx new file mode 100644 index 000000000000..475b2b9160eb --- /dev/null +++ b/filter/source/svg/tokenmap.hxx @@ -0,0 +1,32 @@ +/************************************************************************* + * + * 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 3. + * + ************************************************************************/ + +#ifndef INCLUDED_TOKENMAP_HXX +#define INCLUDED_TOKENMAP_HXX + +#include "tokens.hxx" + +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> + +namespace svgi +{ + sal_Int32 getTokenId( const char* sIdent, sal_Int32 nLen ); + sal_Int32 getTokenId( const rtl::OUString& sIdent ); + const char* getTokenName( sal_Int32 nTokenId ); + +} // namespace svgi + +#endif diff --git a/filter/source/svg/tokens.txt b/filter/source/svg/tokens.txt new file mode 100644 index 000000000000..1211e691f627 --- /dev/null +++ b/filter/source/svg/tokens.txt @@ -0,0 +1,403 @@ +####################################### +# +# elements (SVG Tiny 1.2) +# +####################################### +a +animate +animateColor +animateMotion +animateTransform +animation +audio +circle +defs +desc +discard +ellipse +font +font-face +font-face-src +font-face-uri +foreignObject +g +glyph +handler +hkern +image +line +linearGradient +listener +metadata +missing-glyph +mpath +path +polygon +polyline +prefetch +radialGradient +rect +script +set +solidColor +stop +svg +switch +tbreak +text +textArea +title +tspan +use +video +####################################### +# +# properties (SVG Tiny 1.2) +# +####################################### +audio-level +color +color-rendering +display +display-align +fill +fill-opacity +fill-rule +font-family +font-size +font-style +font-variant +font-weight +image-rendering +line-increment +opacity +pointer-events +shape-rendering +solid-color +solid-opacity +stop-color +stop-opacity +stroke +stroke-dasharray +stroke-dashoffset +stroke-linecap +stroke-linejoin +stroke-miterlimit +stroke-opacity +stroke-width +text-align +text-anchor +text-rendering +vector-effect +viewport-fill +viewport-fill-opacity +visibility +####################################### +# +# attributes (SVG Tiny 1.2) +# +####################################### +accent-height +accumulate +additive +alphabetic +arabic-form +ascent +attributeName +attributeType +bandwidth +baseProfile +bbox +begin +by +calcMode +cap-height +class +contentScriptType +cx +cy +d +defaultAction +descent +dur +editable +end +event +externalResourcesRequired +fill +focusHighlight +focusable +font-family +font-stretch +font-style +font-variant +font-weight +from +fx +fy +g1 +g2 +glyph-name +gradientTransform +gradientUnits +handler +hanging +height +height +horiz-adv-x +horiz-origin-x +id +ideographic +initialVisibility +k +keyPoints +keySplines +keyTimes +lang +mathematical +max +mediaCharacterEncoding +mediaContentEncodings +mediaSize +mediaTime +min +nav-down +nav-down-left +nav-down-right +nav-left +nav-next +nav-prev +nav-right +nav-up +nav-up-left +nav-up-right +observer +offset +origin +overlay +overline-position +overline-thickness +panose-1 +path +pathLength +phase +playbackOrder +points +preserveAspectRatio +propagate +r +repeatCount +repeatDur +requiredExtensions +requiredFeatures +requiredFonts +requiredFormats +restart +rotate +rx +ry +slope +snapshotTime +stemh +stemv +strikethrough-position +strikethrough-thickness +style +syncBehavior +syncBehaviorDefault +syncMaster +syncTolerance +syncToleranceDefault +systemLanguage +target +timelineBegin +to +transform +transformBehavior +type +u1 +u2 +underline-position +underline-thickness +unicode +unicode-range +units-per-em +values +version +viewBox +width +widths +x +x-height +x1 +x2 +actuate +arcrole +href +role +show +base +space +y +y1 +y2 +zoomAndPan +####################################### +# +# colour values +# +####################################### +aliceblue +antiquewhite +aqua +aquamarine +azure +beige +bisque +black +blanchedalmond +blue +blueviolet +brown +burlywood +cadetblue +chartreuse +chocolate +coral +cornflowerblue +cornsilk +crimson +cyan +darkblue +darkcyan +darkgoldenrod +darkgray +darkgreen +darkgrey +darkkhaki +darkmagenta +darkolivegreen +darkorange +darkorchid +darkred +darksalmon +darkseagreen +darkslateblue +darkslategray +darkslategrey +darkturquoise +darkviolet +deeppink +deepskyblue +dimgray +dimgrey +dodgerblue +firebrick +floralwhite +forestgreen +fuchsia +gainsboro +ghostwhite +gold +goldenrod +gray +grey +green +greenyellow +honeydew +hotpink +indianred +indigo +ivory +khaki +lavender +lavenderblush +lawngreen +lemonchiffon +lightblue +lightcoral +lightcyan +lightgoldenrodyellow +lightgray +lightgreen +lightgrey +lightpink +lightsalmon +lightseagreen +lightskyblue +lightslategray +lightslategrey +lightsteelblue +lightyellow +lime +limegreen +linen +magenta +maroon +mediumaquamarine +mediumblue +mediumorchid +mediumpurple +mediumseagreen +mediumslateblue +mediumspringgreen +mediumturquoise +mediumvioletred +midnightblue +mintcream +mistyrose +moccasin +navajowhite +navy +oldlace +olive +olivedrab +orange +orangered +orchid +palegoldenrod +palegreen +paleturquoise +palevioletred +papayawhip +peachpuff +peru +pink +plum +powderblue +purple +red +rosybrown +royalblue +saddlebrown +salmon +sandybrown +seagreen +seashell +sienna +silver +skyblue +slateblue +slategray +slategrey +snow +springgreen +steelblue +tan +teal +thistle +tomato +turquoise +violet +wheat +white +whitesmoke +yellow +yellowgreen +####################################### +# +# Gradient units values +# +####################################### +userSpaceOnUse +objectBoundingBox diff --git a/filter/source/svg/units.cxx b/filter/source/svg/units.cxx new file mode 100644 index 000000000000..b727a42db0fa --- /dev/null +++ b/filter/source/svg/units.cxx @@ -0,0 +1,99 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * Author: + * Jan Holesovsky <kendy@suse.cz> + * 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 3. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_filter.hxx" + +#include "units.hxx" +#include "gfxtypes.hxx" +#include "spirit_supplements.hxx" + +#include <string.h> +#include <rtl/ustring.hxx> + +#include <boost/bind.hpp> +// workaround. spirit uses INT_MAX. +#include <limits.h> +#include <boost/spirit.hpp> + + +namespace svgi +{ + +double convLength( double value, SvgUnit unit, const State& rState, char dir ) +{ + const double fBoxLen( dir=='h' ? rState.maViewBox.getWidth() : + (dir=='v' ? rState.maViewBox.getHeight() : + rState.maViewBox.getRange().getLength())); + + // convert svg unit to internal coordinates ("pixel"). Since the + // OOo drawing layer is still largely integer-based, the initial + // viewport transformation includes a certain scale factor + double fRet(value); + switch ( unit ) + { + case SVG_LENGTH_UNIT_CM: fRet *= 72.0/2.54; break; + case SVG_LENGTH_UNIT_IN: fRet *= 72.0; break; + case SVG_LENGTH_UNIT_MM: fRet *= 72.0/25.4; break; + case SVG_LENGTH_UNIT_PC: fRet *= 72.0/6.0; break; + case SVG_LENGTH_UNIT_USER: + case SVG_LENGTH_UNIT_PX: // no unit defaults to PX in svg, + // assume display to have 72DPI + case SVG_LENGTH_UNIT_PT: break; + case SVG_LENGTH_UNIT_EM: fRet *= rState.mnFontSize; break; + case SVG_LENGTH_UNIT_EX: fRet *= rState.mnFontSize / 2.0; break; + case SVG_LENGTH_UNIT_PERCENTAGE: fRet *= fBoxLen; break; + default: OSL_TRACE( "Unknown length type" ); break; + } + + return fRet; +} + +double convLength( const rtl::OUString& sValue, const State& rState, char dir ) +{ + using namespace ::boost::spirit; + + rtl::OString aUTF8 = rtl::OUStringToOString( sValue, + RTL_TEXTENCODING_UTF8 ); + + double nVal=0.0; + SvgUnit eUnit=SVG_LENGTH_UNIT_PX; + const bool bRes = parse(aUTF8.getStr(), + // Begin grammar + ( + real_p[assign_a(nVal)] + >> ( str_p("cm") [assign_a(eUnit,SVG_LENGTH_UNIT_CM)] + | str_p("em") [assign_a(eUnit,SVG_LENGTH_UNIT_EM)] + | str_p("ex") [assign_a(eUnit,SVG_LENGTH_UNIT_EX)] + | str_p("in") [assign_a(eUnit,SVG_LENGTH_UNIT_IN)] + | str_p("mm") [assign_a(eUnit,SVG_LENGTH_UNIT_MM)] + | str_p("pc") [assign_a(eUnit,SVG_LENGTH_UNIT_PC)] + | str_p("pt") [assign_a(eUnit,SVG_LENGTH_UNIT_PT)] + | str_p("px") [assign_a(eUnit,SVG_LENGTH_UNIT_PX)] + | str_p("%") [assign_a(eUnit,SVG_LENGTH_UNIT_PERCENTAGE)] + | str_p("") [assign_a(eUnit,SVG_LENGTH_UNIT_USER)] + | end_p) + ), + // End grammar + space_p).full; + + if( !bRes ) + return 0.0; + + return convLength(nVal,eUnit,rState,dir); +} + +} // namespace svgi diff --git a/filter/source/svg/units.hxx b/filter/source/svg/units.hxx new file mode 100644 index 000000000000..7bf764178135 --- /dev/null +++ b/filter/source/svg/units.hxx @@ -0,0 +1,60 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * Author: + * Jan Holesovsky <kendy@suse.cz> + * 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 3. + * + ************************************************************************/ + +#ifndef INCLUDED_UNITS_HXX +#define INCLUDED_UNITS_HXX + +#include <sal/config.h> + +namespace rtl{ class OUString; } +namespace svgi +{ + struct State; + enum SvgUnit + { + SVG_LENGTH_UNIT_CM, + SVG_LENGTH_UNIT_EM, + SVG_LENGTH_UNIT_EX, + SVG_LENGTH_UNIT_IN, + SVG_LENGTH_UNIT_MM, + SVG_LENGTH_UNIT_PC, + SVG_LENGTH_UNIT_PT, + SVG_LENGTH_UNIT_PX, + SVG_LENGTH_UNIT_PERCENTAGE, + SVG_LENGTH_UNIT_USER + }; + + /** return svg_length_t in 100th's of mm + @param fVal value to convert + @param unit unit the value is in + @param rState current state (needed for viewport dimensions etc.) + @param dir direction - either 'h' or 'v' for horizonal or vertical, resp. + */ + double convLength( double fVal, SvgUnit unit, const State& rState, char dir ); + + /** return svg_length_t in 100th's of mm + @param sValue value to convert + @param rState current state (needed for viewport dimensions etc.) + @param dir direction - either 'h' or 'v' for horizonal or vertical, resp. + */ + double convLength( const rtl::OUString& sValue, const State& rState, char dir ); + + inline double pt2mm(double fVal) { return fVal*25.4/72.0; } + inline double pt100thmm(double fVal) { return fVal*2540.0/72.0; } + +} // namespace svgi + +#endif |