Hi,
Here are some patches to improve SVG export filter.
0001: Make filter export Gradients and Hatches as SVG <pattern>s.
This tend not to change a visual look but improve semantic structure of a file.
# Actually,hatches go outside of a shape sometimes without this patch.
0003: Make filter export linear and axial gradients as SVG <linearGradient>s.
Use SVG's native gradient instead of polygon fallback.
With this patch, gradient steps of linear and axial gradient are ignored.
In other words, linear and axial gradients are always exported as
"smooth" gradients.
However, as far as I know, the manual steps function is for printing.
So I suppose ignoring gradient steps is safe.
To support native gradients, I need to calculate a bounding box of a gradient.
Currently it is done in OutputDevice::ImplDrawLinearGradient and
OutputDevice::ImplDrawComplexGradient.
So I moved that to Gradient class (0002 patch) to use it in filter code.
0004: Make filter export transparencies as SVG <mask>s.
SVG export filter supports only solid transparency of solid fill (i.e.
color) until now.
The patch extends support to support solid and gradient transparency
of all fill styles.
# Some SVG viewers including WebKit don't support <mask> yet :-(.
The patches are under the LGPLv3+ / MPL.
Cheers,
--
KUROSAWA Takeshi <taken.spc@gmail.com>
From 6254c025823c829e56fe6b5775c91c014eb2b87c Mon Sep 17 00:00:00 2001
From: Takeshi Kurosawa <taken.spc@gmail.com>
Date: Wed, 2 Feb 2011 14:31:59 +0900
Subject: [PATCH 1/3] Export hatches and gradients as SVG <pattern>s
This tend not to change a visual look, but improve semantic structure of a file.
---
filter/source/svg/svgwriter.cxx | 85 +++++++++++++++++++++++++++------------
filter/source/svg/svgwriter.hxx | 3 +
2 files changed, 62 insertions(+), 26 deletions(-)
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 7765ea5..1599cc6 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -39,6 +39,7 @@
static const char aXMLElemG[] = "g";
static const char aXMLElemDefs[] = "defs";
static const char aXMLElemClipPath[] = "clipPath";
+static const char aXMLElemPattern[] = "pattern";
static const char aXMLElemLine[] = "line";
static const char aXMLElemRect[] = "rect";
static const char aXMLElemEllipse[] = "ellipse";
@@ -66,6 +67,7 @@ static const char aXMLAttrRY[] = "ry";
static const char aXMLAttrWidth[] = "width";
static const char aXMLAttrHeight[] = "height";
static const char aXMLAttrPoints[] = "points";
+static const char aXMLAttrPatternUnits[] = "patternUnits";
static const char aXMLAttrXLinkHRef[] = "xlink:href";
static const sal_Unicode pBase64[] =
@@ -483,7 +485,8 @@ SVGActionWriter::SVGActionWriter( SvXMLExport& rExport, SVGFontExport& rFontExpo
mrFontExport( rFontExport ),
mpContext( NULL ),
mbClipAttrChanged( sal_False ),
- mnCurClipId( 1 )
+ mnCurClipId( 1 ),
+ mnCurPatternId( 1 )
{
mpVDev = new VirtualDevice;
mpVDev->EnableOutput( sal_False );
@@ -763,48 +766,81 @@ void SVGActionWriter::ImplWritePolyPolygon( const PolyPolygon& rPolyPoly,
sal_Bo
// -----------------------------------------------------------------------------
-void SVGActionWriter::ImplWriteGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient,
- const NMSP_RTL::OUString* pStyle, sal_uInt32
nWriteFlags )
+void SVGActionWriter::ImplWritePattern( const PolyPolygon& rPolyPoly,
+ const Hatch* pHatch,
+ const Gradient* pGradient,
+ const NMSP_RTL::OUString* pStyle,
+ sal_uInt32 nWriteFlags )
{
if( rPolyPoly.Count() )
{
- SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, TRUE, TRUE );
- FastString aClipId;
- FastString aClipStyle;
+ SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, TRUE, TRUE );
- aClipId += B2UCONST( "clip" );
- aClipId += NMSP_RTL::OUString::valueOf( ImplGetNextClipId() );
+ FastString aPatternId;
+ aPatternId += B2UCONST( "pattern" );
+ aPatternId += GetValueString( ImplGetNextPatternId() );
{
SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, TRUE, TRUE );
- mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aClipId.GetString() );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aPatternId.GetString() );
+
+ Rectangle aRect( ImplMap( rPolyPoly.GetBoundRect() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, GetValueString( aRect.Left() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, GetValueString( aRect.Top() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrWidth, GetValueString(
aRect.GetWidth() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrHeight, GetValueString(
aRect.GetHeight() ) );
+
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrPatternUnits, NMSP_RTL::OUString(
RTL_CONSTASCII_USTRINGPARAM( "userSpaceOnUse") ) );
{
- SvXMLElementExport aElemClipPath( mrExport, XML_NAMESPACE_NONE, aXMLElemClipPath,
TRUE, TRUE );
- ImplWritePolyPolygon( rPolyPoly, sal_False );
+ SvXMLElementExport aElemPattern( mrExport, XML_NAMESPACE_NONE, aXMLElemPattern,
TRUE, TRUE );
+
+ // The origion of a pattern is positioned at (aRect.Left(), aRect.Top())
+ // We need to translate a pattern to (aRect.Left(), aRect.Top())
+ FastString aTransform;
+ aTransform += B2UCONST( "translate" );
+ aTransform += B2UCONST( "(" );
+ aTransform += GetValueString( -aRect.Left() );
+ aTransform += B2UCONST( "," );
+ aTransform += GetValueString( -aRect.Top() );
+ aTransform += B2UCONST( ")" );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTransform,
aTransform.GetString() );
+
+ {
+ SvXMLElementExport aElemG2( mrExport, XML_NAMESPACE_NONE, aXMLElemG, TRUE,
TRUE );
+
+ GDIMetaFile aTmpMtf;
+ if( pHatch )
+ mpVDev->AddHatchActions( rPolyPoly, *pHatch, aTmpMtf );
+ else if ( pGradient )
+ mpVDev->AddGradientActions( rPolyPoly.GetBoundRect(), *pGradient, aTmpMtf
);
+ ImplWriteActions( aTmpMtf, pStyle, nWriteFlags );
+ }
}
}
- // create new context with clippath set
- aClipStyle += B2UCONST( "clip-path:URL(#" );
- aClipStyle += aClipId.GetString();
- aClipStyle += B2UCONST( ")" );
-
- mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aClipStyle.GetString() );
+ FastString aPatternStyle;
+ aPatternStyle += B2UCONST( "fill:url(#" );
+ aPatternStyle += aPatternId.GetString();
+ aPatternStyle += B2UCONST( ")" );
{
- GDIMetaFile aTmpMtf;
- SvXMLElementExport aElemG2( mrExport, XML_NAMESPACE_NONE, aXMLElemG, TRUE, TRUE );
-
- mpVDev->AddGradientActions( rPolyPoly.GetBoundRect(), rGradient, aTmpMtf );
- ImplWriteActions( aTmpMtf, pStyle, nWriteFlags );
+ ImplWritePolyPolygon( rPolyPoly, sal_False, &aPatternStyle.GetString() );
}
}
}
// -----------------------------------------------------------------------------
+void SVGActionWriter::ImplWriteGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient,
+ const NMSP_RTL::OUString* pStyle, sal_uInt32
nWriteFlags )
+{
+ ImplWritePattern( rPolyPoly, NULL, &rGradient, pStyle, nWriteFlags );
+}
+
+// -----------------------------------------------------------------------------
+
void SVGActionWriter::ImplWriteText( const Point& rPos, const String& rText,
const sal_Int32* pDXArray, long nWidth,
const NMSP_RTL::OUString* pStyle )
@@ -1409,10 +1445,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
if( nWriteFlags & SVGWRITER_WRITE_FILL )
{
const MetaHatchAction* pA = (const MetaHatchAction*) pAction;
- GDIMetaFile aTmpMtf;
-
- mpVDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
- ImplWriteActions( aTmpMtf, pStyle, nWriteFlags );
+ ImplWritePattern( pA->GetPolyPolygon(), &pA->GetHatch(), NULL, pStyle,
nWriteFlags );
}
}
break;
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index e1467c3..65bb067 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -165,6 +165,7 @@ private:
SVGAttributeWriter* mpContext;
sal_Bool mbClipAttrChanged;
sal_Int32 mnCurClipId;
+ sal_Int32 mnCurPatternId;
Stack maContextStack;
VirtualDevice* mpVDev;
MapMode maTargetMapMode;
@@ -185,6 +186,7 @@ private:
void ImplWriteRect( const Rectangle& rRect, long nRadX =
0, long nRadY = 0, const ::rtl::OUString* pStyle = NULL );
void ImplWriteEllipse( const Point& rCenter, long nRadX,
long nRadY, const ::rtl::OUString* pStyle = NULL );
void ImplWritePolyPolygon( const PolyPolygon& rPolyPoly,
sal_Bool bLineOnly, const ::rtl::OUString* pStyle = NULL );
+ void ImplWritePattern( const PolyPolygon& rPolyPoly,
const Hatch* pHatch, const Gradient* pGradient, const ::rtl::OUString* pStyle, sal_uInt32
nWriteFlags );
void ImplWriteGradientEx( const PolyPolygon& rPolyPoly,
const Gradient& rGradient, const ::rtl::OUString* pStyle, sal_uInt32 nWriteFlags );
void ImplWriteText( const Point& rPos, const String&
rText, const sal_Int32* pDXArray, long nWidth, const ::rtl::OUString* pStyle = NULL );
void ImplWriteText( const Point& rPos, const String& rText, const sal_Int32*
pDXArray, long nWidth, const ::rtl::OUString* pStyle, Color aTextColor );
@@ -195,6 +197,7 @@ private:
void ImplWriteActions( const GDIMetaFile& rMtf, const
::rtl::OUString* pStyle, sal_uInt32 nWriteFlags );
sal_Int32 ImplGetNextClipId() { return mnCurClipId++; }
+ sal_Int32 ImplGetNextPatternId() { return mnCurPatternId++; }
public:
--
1.7.1
From b3ea2556903c07658e748d2bcb80cfd082b9ad7c Mon Sep 17 00:00:00 2001
From: Takeshi Kurosawa <taken.spc@gmail.com>
Date: Tue, 1 Feb 2011 14:40:58 +0900
Subject: [PATCH] Encapsulate calculation of gradient bounding box to Gradient::GetBoundRect
Export filters need to get gradient bounding box.
---
vcl/inc/vcl/gradient.hxx | 3 +
vcl/source/gdi/gradient.cxx | 93 +++++++++++++++++++++++++++++++++++++++++++
vcl/source/gdi/outdev4.cxx | 91 +++++-------------------------------------
3 files changed, 107 insertions(+), 80 deletions(-)
diff --git a/vcl/inc/vcl/gradient.hxx b/vcl/inc/vcl/gradient.hxx
index 46abd8d..f24ec95 100644
--- a/vcl/inc/vcl/gradient.hxx
+++ b/vcl/inc/vcl/gradient.hxx
@@ -31,6 +31,7 @@
#include <vcl/dllapi.h>
#include <tools/color.hxx>
+#include <tools/gen.hxx>
#include <vcl/vclenum.hxx>
@@ -119,6 +120,8 @@ public:
void SetSteps( USHORT nSteps );
USHORT GetSteps() const { return mpImplGradient->mnStepCount; }
+ void GetBoundRect( const Rectangle& rRect, Rectangle &rBoundRect, Point& rCenter )
const;
+
Gradient& operator=( const Gradient& rGradient );
BOOL operator==( const Gradient& rGradient ) const;
BOOL operator!=( const Gradient& rGradient ) const
diff --git a/vcl/source/gdi/gradient.cxx b/vcl/source/gdi/gradient.cxx
index d11d62e..297f30d 100644
--- a/vcl/source/gdi/gradient.cxx
+++ b/vcl/source/gdi/gradient.cxx
@@ -245,6 +245,99 @@ void Gradient::SetSteps( USHORT nSteps )
// -----------------------------------------------------------------------
+void Gradient::GetBoundRect( const Rectangle& rRect, Rectangle& rBoundRect, Point& rCenter ) const
+{
+ Rectangle aRect( rRect );
+ USHORT nAngle = GetAngle() % 3600;
+
+ if( GetStyle() == GRADIENT_LINEAR || GetStyle() == GRADIENT_AXIAL )
+ {
+ aRect.Left()--;
+ aRect.Top()--;
+ aRect.Right()++;
+ aRect.Bottom()++;
+
+ const double fAngle = nAngle * F_PI1800;
+ const double fWidth = aRect.GetWidth();
+ const double fHeight = aRect.GetHeight();
+ double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) );
+ double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) );
+
+ fDX = ( fDX - fWidth ) * 0.5 + 0.5;
+ fDY = ( fDY - fHeight ) * 0.5 + 0.5;
+
+ aRect.Left() -= (long) fDX;
+ aRect.Right() += (long) fDX;
+ aRect.Top() -= (long) fDY;
+ aRect.Bottom() += (long) fDY;
+
+ rBoundRect = aRect;
+ rCenter = rRect.Center();
+ }
+ else
+ {
+
+ if( GetStyle() == GRADIENT_SQUARE || GetStyle() == GRADIENT_RECT )
+ {
+ const double fAngle = nAngle * F_PI1800;
+ const double fWidth = aRect.GetWidth();
+ const double fHeight = aRect.GetHeight();
+ double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle )
);
+ double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle )
);
+
+ fDX = ( fDX - fWidth ) * 0.5 + 0.5;
+ fDY = ( fDY - fHeight ) * 0.5 + 0.5;
+
+ aRect.Left() -= (long) fDX;
+ aRect.Right() += (long) fDX;
+ aRect.Top() -= (long) fDY;
+ aRect.Bottom() += (long) fDY;
+ }
+
+ Size aSize( aRect.GetSize() );
+
+ if( GetStyle() == GRADIENT_RADIAL )
+ {
+ // Radien-Berechnung fuer Kreis
+ aSize.Width() = (long)(0.5 + sqrt((double)aSize.Width()*(double)aSize.Width() +
(double)aSize.Height()*(double)aSize.Height()));
+ aSize.Height() = aSize.Width();
+ }
+ else if( GetStyle() == GRADIENT_ELLIPTICAL )
+ {
+ // Radien-Berechnung fuer Ellipse
+ aSize.Width() = (long)( 0.5 + (double) aSize.Width() * 1.4142 );
+ aSize.Height() = (long)( 0.5 + (double) aSize.Height() * 1.4142 );
+ }
+ else if( GetStyle() == GRADIENT_SQUARE )
+ {
+ if ( aSize.Width() > aSize.Height() )
+ aSize.Height() = aSize.Width();
+ else
+ aSize.Width() = aSize.Height();
+ }
+
+ // neue Mittelpunkte berechnen
+ long nZWidth = aRect.GetWidth() * (long) GetOfsX() / 100;
+ long nZHeight = aRect.GetHeight() * (long) GetOfsY() / 100;
+ long nBorderX = (long) GetBorder() * aSize.Width() / 100;
+ long nBorderY = (long) GetBorder() * aSize.Height() / 100;
+ rCenter = Point( aRect.Left() + nZWidth, aRect.Top() + nZHeight );
+
+ // Rand beruecksichtigen
+ aSize.Width() -= nBorderX;
+ aSize.Height() -= nBorderY;
+
+ // Ausgaberechteck neu setzen
+ aRect.Left() = rCenter.X() - ( aSize.Width() >> 1 );
+ aRect.Top() = rCenter.Y() - ( aSize.Height() >> 1 );
+
+ aRect.SetSize( aSize );
+ rBoundRect = rRect;
+ }
+}
+
+// -----------------------------------------------------------------------
+
Gradient& Gradient::operator=( const Gradient& rGradient )
{
DBG_CHKTHIS( Gradient, NULL );
diff --git a/vcl/source/gdi/outdev4.cxx b/vcl/source/gdi/outdev4.cxx
index e550225..cc6facf 100644
--- a/vcl/source/gdi/outdev4.cxx
+++ b/vcl/source/gdi/outdev4.cxx
@@ -175,42 +175,25 @@ void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
BOOL bMtf, const PolyPolygon* pClipPolyPoly )
{
// rotiertes BoundRect ausrechnen
- Rectangle aRect = rRect;
- aRect.Left()--;
- aRect.Top()--;
- aRect.Right()++;
- aRect.Bottom()++;
- USHORT nAngle = rGradient.GetAngle() % 3600;
- double fAngle = nAngle * F_PI1800;
- double fWidth = aRect.GetWidth();
- double fHeight = aRect.GetHeight();
- double fDX = fWidth * fabs( cos( fAngle ) ) +
- fHeight * fabs( sin( fAngle ) );
- double fDY = fHeight * fabs( cos( fAngle ) ) +
- fWidth * fabs( sin( fAngle ) );
- fDX = (fDX - fWidth) * 0.5 + 0.5;
- fDY = (fDY - fHeight) * 0.5 + 0.5;
- aRect.Left() -= (long)fDX;
- aRect.Right() += (long)fDX;
- aRect.Top() -= (long)fDY;
- aRect.Bottom() += (long)fDY;
+ Rectangle aRect;
+ Point aCenter;
+ USHORT nAngle = rGradient.GetAngle() % 3600;
+
+ rGradient.GetBoundRect( rRect, aRect, aCenter );
// Rand berechnen und Rechteck neu setzen
- Point aCenter = rRect.Center();
Rectangle aFullRect = aRect;
long nBorder = (long)rGradient.GetBorder() * aRect.GetHeight() / 100;
- BOOL bLinear;
// Rand berechnen und Rechteck neu setzen fuer linearen Farbverlauf
- if ( rGradient.GetStyle() == GRADIENT_LINEAR )
+ bool bLinear = (rGradient.GetStyle() == GRADIENT_LINEAR);
+ if ( bLinear )
{
- bLinear = TRUE;
aRect.Top() += nBorder;
}
// Rand berechnen und Rechteck neu setzen fuer axiale Farbverlauf
else
{
- bLinear = FALSE;
nBorder >>= 1;
aRect.Top() += nBorder;
@@ -430,7 +413,8 @@ void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
// Virtuelle Device werden auch ausgeklammert, da einige Treiber
// ansonsten zu langsam sind
PolyPolygon* pPolyPoly;
- Rectangle aRect( rRect );
+ Rectangle aRect;
+ Point aCenter;
Color aStartCol( rGradient.GetStartColor() );
Color aEndCol( rGradient.GetEndColor() );
long nStartRed = ( (long) aStartCol.GetRed() *
rGradient.GetStartIntensity() ) / 100;
@@ -444,67 +428,14 @@ void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
long nBlueSteps = nEndBlue - nStartBlue;
long nStepCount = rGradient.GetSteps();
USHORT nAngle = rGradient.GetAngle() % 3600;
+
+ rGradient.GetBoundRect( rRect, aRect, aCenter );
if( (meRasterOp != ROP_OVERPAINT) || (meOutDevType != OUTDEV_WINDOW) || bMtf )
pPolyPoly = new PolyPolygon( 2 );
else
pPolyPoly = NULL;
- if( rGradient.GetStyle() == GRADIENT_SQUARE || rGradient.GetStyle() == GRADIENT_RECT )
- {
- const double fAngle = nAngle * F_PI1800;
- const double fWidth = aRect.GetWidth();
- const double fHeight = aRect.GetHeight();
- double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) );
- double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) );
-
- fDX = ( fDX - fWidth ) * 0.5 + 0.5;
- fDY = ( fDY - fHeight ) * 0.5 + 0.5;
-
- aRect.Left() -= (long) fDX;
- aRect.Right() += (long) fDX;
- aRect.Top() -= (long) fDY;
- aRect.Bottom() += (long) fDY;
- }
-
- Size aSize( aRect.GetSize() );
-
- if( rGradient.GetStyle() == GRADIENT_RADIAL )
- {
- // Radien-Berechnung fuer Kreis
- aSize.Width() = (long)(0.5 + sqrt((double)aSize.Width()*(double)aSize.Width() +
(double)aSize.Height()*(double)aSize.Height()));
- aSize.Height() = aSize.Width();
- }
- else if( rGradient.GetStyle() == GRADIENT_ELLIPTICAL )
- {
- // Radien-Berechnung fuer Ellipse
- aSize.Width() = (long)( 0.5 + (double) aSize.Width() * 1.4142 );
- aSize.Height() = (long)( 0.5 + (double) aSize.Height() * 1.4142 );
- }
- else if( rGradient.GetStyle() == GRADIENT_SQUARE )
- {
- if ( aSize.Width() > aSize.Height() )
- aSize.Height() = aSize.Width();
- else
- aSize.Width() = aSize.Height();
- }
-
- // neue Mittelpunkte berechnen
- long nZWidth = aRect.GetWidth() * (long) rGradient.GetOfsX() / 100;
- long nZHeight = aRect.GetHeight() * (long) rGradient.GetOfsY() / 100;
- long nBorderX = (long) rGradient.GetBorder() * aSize.Width() / 100;
- long nBorderY = (long) rGradient.GetBorder() * aSize.Height() / 100;
- Point aCenter( aRect.Left() + nZWidth, aRect.Top() + nZHeight );
-
- // Rand beruecksichtigen
- aSize.Width() -= nBorderX;
- aSize.Height() -= nBorderY;
-
- // Ausgaberechteck neu setzen
- aRect.Left() = aCenter.X() - ( aSize.Width() >> 1 );
- aRect.Top() = aCenter.Y() - ( aSize.Height() >> 1 );
-
- aRect.SetSize( aSize );
long nMinRect = Min( aRect.GetWidth(), aRect.GetHeight() );
// Anzahl der Schritte berechnen, falls nichts uebergeben wurde
--
1.7.1
From 9ef0b3c56099a6c58a1347574115d803c2071ee6 Mon Sep 17 00:00:00 2001
From: Takeshi Kurosawa <taken.spc@gmail.com>
Date: Wed, 2 Feb 2011 14:35:20 +0900
Subject: [PATCH 2/3] Export linear and axial gradient as SVG <linearGradient>
With this patch, gradient steps of linear and axial are ignored.
In other words, theose gradients are always exported as "smooth" gradients.
---
filter/source/svg/svgwriter.cxx | 141 ++++++++++++++++++++++++++++++++++-----
filter/source/svg/svgwriter.hxx | 5 ++
2 files changed, 130 insertions(+), 16 deletions(-)
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 1599cc6..ab5bd3b 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -40,6 +40,8 @@ static const char aXMLElemG[] = "g";
static const char aXMLElemDefs[] = "defs";
static const char aXMLElemClipPath[] = "clipPath";
static const char aXMLElemPattern[] = "pattern";
+static const char aXMLElemLinearGradient[] = "linearGradient";
+static const char aXMLElemStop[] = "stop";
static const char aXMLElemLine[] = "line";
static const char aXMLElemRect[] = "rect";
static const char aXMLElemEllipse[] = "ellipse";
@@ -68,6 +70,8 @@ static const char aXMLAttrWidth[] = "width";
static const char aXMLAttrHeight[] = "height";
static const char aXMLAttrPoints[] = "points";
static const char aXMLAttrPatternUnits[] = "patternUnits";
+static const char aXMLAttrGradientUnits[] = "gradientUnits";
+static const char aXMLAttrOffset[] = "offset";
static const char aXMLAttrXLinkHRef[] = "xlink:href";
static const sal_Unicode pBase64[] =
@@ -331,6 +335,22 @@ NMSP_RTL::OUString SVGAttributeWriter::GetFontStyle( const Font& rFont )
// -----------------------------------------------------------------------------
+NMSP_RTL::OUString SVGAttributeWriter::GetColorStyle( const Color& rColor,
+ USHORT nIntensity )
+{
+ FastString aStyle;
+ aStyle += B2UCONST( "rgb(" );
+ aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rColor.GetRed() * nIntensity / 100L );
+ aStyle += B2UCONST( "," );
+ aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rColor.GetGreen() * nIntensity / 100L );
+ aStyle += B2UCONST( "," );
+ aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rColor.GetBlue() * nIntensity / 100L );
+ aStyle += B2UCONST( ")" );
+ return aStyle.GetString();
+}
+
+// -----------------------------------------------------------------------------
+
NMSP_RTL::OUString SVGAttributeWriter::GetPaintStyle( const Color& rLineColor, const Color&
rFillColor, const LineInfo* pLineInfo )
{
FastString aStyle;
@@ -343,13 +363,7 @@ NMSP_RTL::OUString SVGAttributeWriter::GetPaintStyle( const Color& rLineColor,
c
else
{
// line color value in rgb
- aStyle += B2UCONST( "rgb(" );
- aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rLineColor.GetRed() );
- aStyle += B2UCONST( "," );
- aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rLineColor.GetGreen() );
- aStyle += B2UCONST( "," );
- aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rLineColor.GetBlue() );
- aStyle += B2UCONST( ")" );
+ aStyle += GetColorStyle( rLineColor );
// line color opacity in percent if neccessary
if( rLineColor.GetTransparency() )
@@ -431,13 +445,7 @@ NMSP_RTL::OUString SVGAttributeWriter::GetPaintStyle( const Color& rLineColor,
c
else
{
// fill color value in rgb
- aStyle += B2UCONST( "rgb(" );
- aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rFillColor.GetRed() );
- aStyle += B2UCONST( "," );
- aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rFillColor.GetGreen() );
- aStyle += B2UCONST( "," );
- aStyle += NMSP_RTL::OUString::valueOf( (sal_Int32) rFillColor.GetBlue() );
- aStyle += B2UCONST( ")" );
+ aStyle += GetColorStyle( rFillColor );
// fill color opacity in percent if neccessary
if( rFillColor.GetTransparency() )
@@ -486,7 +494,8 @@ SVGActionWriter::SVGActionWriter( SvXMLExport& rExport, SVGFontExport& rFontExpo
mpContext( NULL ),
mbClipAttrChanged( sal_False ),
mnCurClipId( 1 ),
- mnCurPatternId( 1 )
+ mnCurPatternId( 1 ),
+ mnCurGradientId( 1 )
{
mpVDev = new VirtualDevice;
mpVDev->EnableOutput( sal_False );
@@ -836,7 +845,107 @@ void SVGActionWriter::ImplWritePattern( const PolyPolygon& rPolyPoly,
void SVGActionWriter::ImplWriteGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient,
const NMSP_RTL::OUString* pStyle, sal_uInt32
nWriteFlags )
{
- ImplWritePattern( rPolyPoly, NULL, &rGradient, pStyle, nWriteFlags );
+ if ( rGradient.GetStyle() == GRADIENT_LINEAR ||
+ rGradient.GetStyle() == GRADIENT_AXIAL )
+ {
+ ImplWriteGradientLinear( rPolyPoly, rGradient );
+ }
+ else
+ {
+ ImplWritePattern( rPolyPoly, NULL, &rGradient, pStyle, nWriteFlags );
+ }
+}
+
+void SVGActionWriter::ImplWriteGradientLinear( const PolyPolygon& rPolyPoly,
+ const Gradient& rGradient )
+{
+ if( rPolyPoly.Count() )
+ {
+ SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, TRUE, TRUE );
+
+ FastString aGradientId;
+ aGradientId += B2UCONST( "gradient" );
+ aGradientId += GetValueString( ImplGetNextGradientId() );
+
+ {
+ SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, TRUE, TRUE );
+
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aGradientId.GetString() );
+ {
+ Rectangle aTmpRect;
+ Point aTmpCenter;
+ rGradient.GetBoundRect( rPolyPoly.GetBoundRect(), aTmpRect, aTmpCenter );
+ const Rectangle aRect( ImplMap( aTmpRect) );
+ const Point aCenter( ImplMap( aTmpCenter) );
+ const USHORT nAngle = rGradient.GetAngle() % 3600;
+
+ Polygon aPoly( 2 );
+ aPoly[ 0 ].X() = aPoly[ 1 ].X() = aRect.Left();
+ aPoly[ 0 ].Y() = aRect.Top();
+ aPoly[ 1 ].Y() = aRect.Bottom();
+ aPoly.Rotate( aCenter, nAngle );
+
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX1, GetValueString( aPoly[ 0
].X() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY1, GetValueString( aPoly[ 0
].Y() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX2, GetValueString( aPoly[ 1
].X() ) );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY2, GetValueString( aPoly[ 1
].Y() ) );
+
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrGradientUnits,
NMSP_RTL::OUString( RTL_CONSTASCII_USTRINGPARAM( "userSpaceOnUse" ) ) );
+ }
+
+ {
+ SvXMLElementExport aElemLinearGradient( mrExport, XML_NAMESPACE_NONE,
aXMLElemLinearGradient, TRUE, TRUE );
+
+ if( rGradient.GetStyle() == GRADIENT_LINEAR )
+ {
+ ImplWriteGradientStop( rGradient.GetStartColor(),
+ rGradient.GetStartIntensity(),
+ rGradient.GetBorder() / 100.0 );
+ ImplWriteGradientStop( rGradient.GetEndColor(),
+ rGradient.GetEndIntensity(),
+ 1.0 );
+ }
+ else
+ {
+ ImplWriteGradientStop( rGradient.GetEndColor(),
+ rGradient.GetEndIntensity(),
+ rGradient.GetBorder() / 200.0 );
+ ImplWriteGradientStop( rGradient.GetStartColor(),
+ rGradient.GetStartIntensity(),
+ 0.5 );
+ ImplWriteGradientStop( rGradient.GetEndColor(),
+ rGradient.GetEndIntensity(),
+ 1.0 - rGradient.GetBorder() / 200.0 );
+ }
+ }
+ }
+
+ FastString aGradientStyle;
+ aGradientStyle += B2UCONST( "fill:" );
+ aGradientStyle += B2UCONST( "url(#" );
+ aGradientStyle += aGradientId.GetString();
+ aGradientStyle += B2UCONST( ")" );
+
+ {
+ ImplWritePolyPolygon( rPolyPoly, sal_False, &aGradientStyle.GetString() );
+ }
+ }
+}
+
+void SVGActionWriter::ImplWriteGradientStop( const Color& rColor,
+ USHORT nIntensity,
+ double fOffset )
+{
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, NMSP_RTL::OUString::valueOf(
fOffset ) );
+
+ FastString aStyle;
+ aStyle += B2UCONST( "stop-color:" );
+ aStyle += mpContext->GetColorStyle ( rColor, nIntensity );
+
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aStyle.GetString() );
+ {
+ SvXMLElementExport aElemStartStop( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, TRUE, TRUE
);
+ }
}
// -----------------------------------------------------------------------------
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index 65bb067..103bdee 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -142,6 +142,7 @@ public:
virtual ~SVGAttributeWriter();
::rtl::OUString GetFontStyle( const Font& rFont );
+ ::rtl::OUString GetColorStyle( const Color& rColor, USHORT nItensity = 100
);
::rtl::OUString GetPaintStyle( const Color& rLineColor, const Color&
rFillColor, const LineInfo* pLineInfo );
void SetFontAttr( const Font& rFont );
@@ -166,6 +167,7 @@ private:
sal_Bool mbClipAttrChanged;
sal_Int32 mnCurClipId;
sal_Int32 mnCurPatternId;
+ sal_Int32 mnCurGradientId;
Stack maContextStack;
VirtualDevice* mpVDev;
MapMode maTargetMapMode;
@@ -188,6 +190,8 @@ private:
void ImplWritePolyPolygon( const PolyPolygon& rPolyPoly,
sal_Bool bLineOnly, const ::rtl::OUString* pStyle = NULL );
void ImplWritePattern( const PolyPolygon& rPolyPoly,
const Hatch* pHatch, const Gradient* pGradient, const ::rtl::OUString* pStyle, sal_uInt32
nWriteFlags );
void ImplWriteGradientEx( const PolyPolygon& rPolyPoly,
const Gradient& rGradient, const ::rtl::OUString* pStyle, sal_uInt32 nWriteFlags );
+ void ImplWriteGradientLinear( const PolyPolygon&
rPolyPoly, const Gradient& rGradient );
+ void ImplWriteGradientStop( const Color& rColor, USHORT
nIntensity, double fOffset );
void ImplWriteText( const Point& rPos, const String&
rText, const sal_Int32* pDXArray, long nWidth, const ::rtl::OUString* pStyle = NULL );
void ImplWriteText( const Point& rPos, const String& rText, const sal_Int32*
pDXArray, long nWidth, const ::rtl::OUString* pStyle, Color aTextColor );
void ImplWriteBmp( const BitmapEx& rBmpEx, const Point&
rPt, const Size& rSz, const Point& rSrcPt, const Size& rSrcSz, const ::rtl::OUString* pStyle = NULL
);
@@ -198,6 +202,7 @@ private:
void ImplWriteActions( const GDIMetaFile& rMtf, const
::rtl::OUString* pStyle, sal_uInt32 nWriteFlags );
sal_Int32 ImplGetNextClipId() { return mnCurClipId++; }
sal_Int32 ImplGetNextPatternId() { return mnCurPatternId++; }
+ sal_Int32 ImplGetNextGradientId() { return mnCurGradientId++; }
public:
--
1.7.1
From 84ae44fd034700fb9c55000bf85ca893c06edd67 Mon Sep 17 00:00:00 2001
From: Takeshi Kurosawa <taken.spc@gmail.com>
Date: Wed, 2 Feb 2011 14:38:31 +0900
Subject: [PATCH 3/3] Export transparencies as SVg <mask>s
SVG export filter supports only solid transparency of solid fill (i.e. color) until now.
This patch extends support to solid and gradient transparency of all fill styles.
---
filter/source/svg/svgwriter.cxx | 94 +++++++++++++++++++++++++++++---------
filter/source/svg/svgwriter.hxx | 3 +
2 files changed, 74 insertions(+), 23 deletions(-)
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index ab5bd3b..f88c36c 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -39,6 +39,7 @@
static const char aXMLElemG[] = "g";
static const char aXMLElemDefs[] = "defs";
static const char aXMLElemClipPath[] = "clipPath";
+static const char aXMLElemMask[] = "mask";
static const char aXMLElemPattern[] = "pattern";
static const char aXMLElemLinearGradient[] = "linearGradient";
static const char aXMLElemStop[] = "stop";
@@ -495,7 +496,8 @@ SVGActionWriter::SVGActionWriter( SvXMLExport& rExport, SVGFontExport& rFontExpo
mbClipAttrChanged( sal_False ),
mnCurClipId( 1 ),
mnCurPatternId( 1 ),
- mnCurGradientId( 1 )
+ mnCurGradientId( 1 ),
+ mnCurMaskId( 1 )
{
mpVDev = new VirtualDevice;
mpVDev->EnableOutput( sal_False );
@@ -949,6 +951,72 @@ void SVGActionWriter::ImplWriteGradientStop( const Color& rColor,
}
// -----------------------------------------------------------------------------
+void SVGActionWriter::ImplWriteMask( GDIMetaFile& rMtf,
+ const Point& rDestPt,
+ const Size& rDestSize,
+ const Gradient& rGradient,
+ const NMSP_RTL::OUString* pStyle,
+ sal_uInt32 nWriteFlags )
+{
+ Point aSrcPt( rMtf.GetPrefMapMode().GetOrigin() );
+ const Size aSrcSize( rMtf.GetPrefSize() );
+ const double fScaleX = aSrcSize.Width() ? (double) rDestSize.Width() / aSrcSize.Width() :
1.0;
+ const double fScaleY = aSrcSize.Height() ? (double) rDestSize.Height() / aSrcSize.Height() :
1.0;
+ long nMoveX, nMoveY;
+
+ if( fScaleX != 1.0 || fScaleY != 1.0 )
+ {
+ rMtf.Scale( fScaleX, fScaleY );
+ aSrcPt.X() = FRound( aSrcPt.X() * fScaleX ), aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY );
+ }
+
+ nMoveX = rDestPt.X() - aSrcPt.X(), nMoveY = rDestPt.Y() - aSrcPt.Y();
+
+ if( nMoveX || nMoveY )
+ rMtf.Move( nMoveX, nMoveY );
+
+ FastString aMaskId;
+ aMaskId += B2UCONST( "mask" );
+ aMaskId += GetValueString( ImplGetNextMaskId() );
+
+ {
+ SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, TRUE, TRUE );
+
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aMaskId.GetString() );
+ {
+ SvXMLElementExport aElemMask( mrExport, XML_NAMESPACE_NONE, aXMLElemMask, TRUE, TRUE );
+
+ PolyPolygon aPolyPolygon( PolyPolygon( Rectangle( rDestPt, rDestSize ) ) );
+ Gradient aGradient( rGradient );
+
+ // swap gradient stops to adopt SVG mask
+ Color tmpColor( aGradient.GetStartColor() );
+ USHORT tmpIntensity( aGradient.GetStartIntensity() );
+ aGradient.SetStartColor( aGradient.GetEndColor() );
+ aGradient.SetStartIntensity( aGradient.GetEndIntensity()) ;
+ aGradient.SetEndColor( tmpColor );
+ aGradient.SetEndIntensity( tmpIntensity );
+
+ ImplWriteGradientEx( aPolyPolygon, aGradient, pStyle, nWriteFlags );
+ }
+ }
+
+ FastString aMaskStyle;
+ aMaskStyle += B2UCONST( "mask:url(#" );
+ aMaskStyle += aMaskId.GetString();
+ aMaskStyle += B2UCONST( ")" );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aMaskStyle.GetString() );
+
+ {
+ SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, TRUE, TRUE );
+
+ mpVDev->Push();
+ ImplWriteActions( rMtf, pStyle, nWriteFlags );
+ mpVDev->Pop();
+ }
+}
+
+// -----------------------------------------------------------------------------
void SVGActionWriter::ImplWriteText( const Point& rPos, const String& rText,
const sal_Int32* pDXArray, long nWidth,
@@ -1588,28 +1656,8 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
{
const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)
pAction;
GDIMetaFile aTmpMtf(
pA->GetGDIMetaFile() );
- Point aSrcPt(
aTmpMtf.GetPrefMapMode().GetOrigin() );
- const Size aSrcSize(
aTmpMtf.GetPrefSize() );
- const Point aDestPt(
pA->GetPoint() );
- const Size aDestSize(
pA->GetSize() );
- const double fScaleX =
aSrcSize.Width() ? (double) aDestSize.Width() / aSrcSize.Width() : 1.0;
- const double fScaleY =
aSrcSize.Height() ? (double) aDestSize.Height() / aSrcSize.Height() : 1.0;
- long nMoveX,
nMoveY;
-
- if( fScaleX != 1.0 || fScaleY != 1.0 )
- {
- aTmpMtf.Scale( fScaleX, fScaleY );
- aSrcPt.X() = FRound( aSrcPt.X() * fScaleX ), aSrcPt.Y() = FRound(
aSrcPt.Y() * fScaleY );
- }
-
- nMoveX = aDestPt.X() - aSrcPt.X(), nMoveY = aDestPt.Y() - aSrcPt.Y();
-
- if( nMoveX || nMoveY )
- aTmpMtf.Move( nMoveX, nMoveY );
-
- mpVDev->Push();
- ImplWriteActions( aTmpMtf, pStyle, nWriteFlags );
- mpVDev->Pop();
+ ImplWriteMask( aTmpMtf, pA->GetPoint(), pA->GetSize(),
+ pA->GetGradient(), pStyle, nWriteFlags );
}
}
break;
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index 103bdee..90fae3d 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -168,6 +168,7 @@ private:
sal_Int32 mnCurClipId;
sal_Int32 mnCurPatternId;
sal_Int32 mnCurGradientId;
+ sal_Int32 mnCurMaskId;
Stack maContextStack;
VirtualDevice* mpVDev;
MapMode maTargetMapMode;
@@ -192,6 +193,7 @@ private:
void ImplWriteGradientEx( const PolyPolygon& rPolyPoly,
const Gradient& rGradient, const ::rtl::OUString* pStyle, sal_uInt32 nWriteFlags );
void ImplWriteGradientLinear( const PolyPolygon&
rPolyPoly, const Gradient& rGradient );
void ImplWriteGradientStop( const Color& rColor, USHORT
nIntensity, double fOffset );
+ void ImplWriteMask( GDIMetaFile& rMtf, const Point&
rDestPt, const Size& rDestSize, const Gradient& rGradient, const ::rtl::OUString* pStyle,
sal_uInt32 nWriteFlags );
void ImplWriteText( const Point& rPos, const String&
rText, const sal_Int32* pDXArray, long nWidth, const ::rtl::OUString* pStyle = NULL );
void ImplWriteText( const Point& rPos, const String& rText, const sal_Int32*
pDXArray, long nWidth, const ::rtl::OUString* pStyle, Color aTextColor );
void ImplWriteBmp( const BitmapEx& rBmpEx, const Point&
rPt, const Size& rSz, const Point& rSrcPt, const Size& rSrcSz, const ::rtl::OUString* pStyle = NULL
);
@@ -203,6 +205,7 @@ private:
sal_Int32 ImplGetNextClipId() { return mnCurClipId++; }
sal_Int32 ImplGetNextPatternId() { return mnCurPatternId++; }
sal_Int32 ImplGetNextGradientId() { return mnCurGradientId++; }
+ sal_Int32 ImplGetNextMaskId() { return mnCurMaskId++; }
public:
--
1.7.1
Context
- [Libreoffice] [Patch] SVG export filter improvements · KUROSAWA, Takeshi
Privacy Policy |
Impressum (Legal Info) |
Copyright information: Unless otherwise specified, all text and images
on this website are licensed under the
Creative Commons Attribution-Share Alike 3.0 License.
This does not include the source code of LibreOffice, which is
licensed under the Mozilla Public License (
MPLv2).
"LibreOffice" and "The Document Foundation" are
registered trademarks of their corresponding registered owners or are
in actual use as trademarks in one or more countries. Their respective
logos and icons are also subject to international copyright laws. Use
thereof is explained in our
trademark policy.