summaryrefslogtreecommitdiff
path: root/oox
diff options
context:
space:
mode:
Diffstat (limited to 'oox')
-rw-r--r--oox/Library_oox.mk6
-rw-r--r--oox/source/drawingml/fillproperties.cxx212
2 files changed, 192 insertions, 26 deletions
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index f356565dbef8..17fb3983b0a6 100644
--- a/oox/Library_oox.mk
+++ b/oox/Library_oox.mk
@@ -59,16 +59,18 @@ $(eval $(call gb_Library_use_libraries,oox,\
$(gb_UWINAPI) \
))
-ifeq ($(TLS),OPENSSL)
$(eval $(call gb_Library_use_externals,oox,\
boost_headers \
+))
+
+ifeq ($(TLS),OPENSSL)
+$(eval $(call gb_Library_use_externals,oox,\
openssl \
openssl_headers \
))
else
ifeq ($(TLS),NSS)
$(eval $(call gb_Library_use_externals,oox,\
- boost_headers \
plc4 \
nss3 \
))
diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx
index 6507e6fac8ff..361abf8e76b7 100644
--- a/oox/source/drawingml/fillproperties.cxx
+++ b/oox/source/drawingml/fillproperties.cxx
@@ -17,6 +17,9 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
+#include <iterator>
+#include <boost/utility.hpp>
+
#include "oox/drawingml/fillproperties.hxx"
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
@@ -330,8 +333,8 @@ void FillProperties::pushToPropMap( ShapePropertyMap& rPropMap,
aGradient.StartIntensity = 100;
aGradient.EndIntensity = 100;
- size_t nColorCount = maGradientProps.maGradientStops.size();
- if( nColorCount > 1 )
+ // Old code, values in aGradient overwritten in many cases by newer code below
+ if( maGradientProps.maGradientStops.size() > 1 )
{
aGradient.StartColor = maGradientProps.maGradientStops.begin()->second.getColor( rGraphicHelper, nPhClr );
aGradient.EndColor = maGradientProps.maGradientStops.rbegin()->second.getColor( rGraphicHelper, nPhClr );
@@ -367,33 +370,194 @@ void FillProperties::pushToPropMap( ShapePropertyMap& rPropMap,
}
else
{
- /* Try to detect a VML axial gradient. This type of
- gradient is simulated by a 3-point linear gradient.
- Even if it's a multi-color linear gradient, its probably better to assume
- axial gradient, when there are 3 or more points.
- */
- bool bAxial = (nColorCount >= 3);
- aGradient.Style = bAxial ? awt::GradientStyle_AXIAL : awt::GradientStyle_LINEAR;
- nDmlAngle = maGradientProps.moShadeAngle.get( 0 ) - nShapeRotation;
- // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
- aGradient.Angle = static_cast< sal_Int16 >( (4500 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 );
- if( bAxial )
+ // A copy of the gradient stops for local modification
+ GradientFillProperties::GradientStopMap aGradientStops(maGradientProps.maGradientStops);
+
+ // Add a fake gradient stop at 0% and 100% if necessary, so that the gradient always starts
+ // at 0% and ends at 100%, to make following logic clearer (?).
+ if( aGradientStops.find(0.0) == aGradientStops.end() )
+ {
+ // temp variable required
+ Color aFirstColor(aGradientStops.begin()->second);
+ aGradientStops[0.0] = aFirstColor;
+ }
+
+ if( aGradientStops.find(1.0) == aGradientStops.end() )
+ {
+ // ditto
+ Color aLastColor(aGradientStops.rbegin()->second);
+ aGradientStops[1.0] = aLastColor;
+ }
+
+ // Check if the gradient is symmetric, which we will emulate with an "axial" gradient.
+ bool bSymmetric(true);
+ {
+ GradientFillProperties::GradientStopMap::const_iterator aItA( aGradientStops.begin() );
+ GradientFillProperties::GradientStopMap::const_iterator aItZ( boost::prior( aGradientStops.end() ) );
+ while( bSymmetric && aItA->first < aItZ->first )
+ {
+ if( aItA->second.getColor( rGraphicHelper, nPhClr ) != aItZ->second.getColor( rGraphicHelper, nPhClr ) ||
+ aItA->second.getTransparency() != aItZ->second.getTransparency() )
+ bSymmetric = false;
+ else
+ {
+ aItA++;
+ aItZ = boost::prior(aItZ);
+ }
+ }
+ // Don't be fooled if the middlemost stop isn't at 0.5.
+ if( bSymmetric && aItA == aItZ && aItA->first != 0.5 )
+ bSymmetric = false;
+
+ // If symmetric, do the rest of the logic for just a half.
+ if( bSymmetric )
+ {
+ // aItZ already points to the colour for the middle, but insert a fake stop at the
+ // exact middle if necessary.
+ if( aItA->first != aItZ->first )
+ {
+ Color aMiddleColor = aItZ->second;
+ aGradientStops[0.5] = aMiddleColor;
+ }
+ // Drop the rest of the stops
+ while( aGradientStops.rbegin()->first > 0.5 )
+ aGradientStops.erase( aGradientStops.rbegin()->first );
+ }
+ }
+
+ SAL_INFO("oox.drawingml.gradient", "symmetric: " << (bSymmetric ? "YES" : "NO") <<
+ ", number of stops: " << aGradientStops.size());
+ for (GradientFillProperties::GradientStopMap::iterator p(aGradientStops.begin());
+ p != aGradientStops.end();
+ p++)
+ SAL_INFO("oox.drawingml.gradient", " " << std::distance(aGradientStops.begin(), p) << ": " <<
+ p->first << ": " <<
+ std::hex << p->second.getColor( rGraphicHelper, nPhClr ) << std::dec <<
+ "@" << (100-p->second.getTransparency()) << "%");
+
+ // Now estimate the simple LO style gradient (only two stops, at n% and 100%, where n ==
+ // the "border") that best emulates the gradient between begin() and prior(end()).
+
+ // First look for the largest segment in the gradient.
+ GradientFillProperties::GradientStopMap::const_iterator aIt(aGradientStops.begin());
+ double nWidestWidth = -1;
+ GradientFillProperties::GradientStopMap::const_iterator aWidestSegmentStart;
+ aIt++;
+ while( aIt != aGradientStops.end() )
+ {
+ if( aIt->first - boost::prior(aIt)->first > nWidestWidth )
+ {
+ nWidestWidth = aIt->first - boost::prior(aIt)->first;
+ aWidestSegmentStart = boost::prior(aIt);
+ }
+ aIt++;
+ }
+ assert( nWidestWidth > 0 );
+
+ double nBorder = 0;
+ bool bSwap(false);
+
+ // Do we have just two segments, and either one is of uniform colour, or three or more
+ // segments, and the widest one is the first or last one, and is it of uniform colour? If
+ // so, deduce the border from it, and drop that segment.
+ if( aGradientStops.size() == 3 &&
+ aGradientStops.begin()->second.getColor( rGraphicHelper, nPhClr ) == boost::next(aGradientStops.begin())->second.getColor( rGraphicHelper, nPhClr ) &&
+ aGradientStops.begin()->second.getTransparency() == boost::next(aGradientStops.begin())->second.getTransparency( ) )
+ {
+ // Two segments, first is uniformly coloured
+ SAL_INFO("oox.drawingml.gradient", "two segments, first is uniformly coloured");
+ nBorder = boost::next(aGradientStops.begin())->first - aGradientStops.begin()->first;
+ aGradientStops.erase(aGradientStops.begin());
+ aWidestSegmentStart = aGradientStops.begin();
+ }
+ else if( !bSymmetric &&
+ aGradientStops.size() == 3 &&
+ boost::next(aGradientStops.begin())->second.getColor( rGraphicHelper, nPhClr ) == boost::prior(aGradientStops.end())->second.getColor( rGraphicHelper, nPhClr ) &&
+ boost::next(aGradientStops.begin())->second.getTransparency() == boost::prior(aGradientStops.end())->second.getTransparency( ) )
{
- GradientFillProperties::GradientStopMap::const_iterator aIt = maGradientProps.maGradientStops.begin();
- // Try to find the axial median
- for(size_t i=0;i<nColorCount;i+=3)
- ++aIt;
- // API StartColor is inner color in axial gradient
- // aIt->second.hasColor() kind would be better than Color != API_RGB_WHITE
- if( aGradient.StartColor == aGradient.EndColor &&
- ( !aIt->second.hasTransparency() || aIt->second.getColor( rGraphicHelper, nPhClr ) != API_RGB_WHITE ) )
+ // Two segments, second is uniformly coloured
+ SAL_INFO("oox.drawingml.gradient", "two segments, second is uniformly coloured");
+ nBorder = boost::prior(aGradientStops.end())->first - boost::next(aGradientStops.begin())->first;
+ aGradientStops.erase(boost::next(aGradientStops.begin()));
+ aWidestSegmentStart = aGradientStops.begin();
+ bSwap = true;
+ nShapeRotation = 180*60000 - nShapeRotation;
+ }
+ else if( !bSymmetric &&
+ aGradientStops.size() >= 4 &&
+ aWidestSegmentStart->second.getColor( rGraphicHelper, nPhClr ) == boost::next(aWidestSegmentStart)->second.getColor( rGraphicHelper, nPhClr ) &&
+ aWidestSegmentStart->second.getTransparency() == boost::next(aWidestSegmentStart)->second.getTransparency() &&
+ ( aWidestSegmentStart == aGradientStops.begin() ||
+ boost::next(aWidestSegmentStart) == boost::prior( aGradientStops.end() ) ) )
+ {
+ // Not symmetric, three or more segments, the widest is first or last and is uniformly coloured
+ SAL_INFO("oox.drawingml.gradient", "first or last segment is widest and is uniformly coloured");
+ nBorder = boost::next(aWidestSegmentStart)->first - aWidestSegmentStart->first;
+
+ // If it's the last segment that is uniformly coloured, rotate the gradient 180
+ // degrees and swap start and end colours
+ if( boost::next(aWidestSegmentStart) == boost::prior( aGradientStops.end() ) )
{
- aGradient.StartColor = aIt->second.getColor( rGraphicHelper, nPhClr );
+ bSwap = true;
+ nShapeRotation = 180*60000 - nShapeRotation;
}
- if( nStartTrans == nEndTrans && aIt->second.hasTransparency() )
- nStartTrans = aIt->second.getTransparency()*255/100;
+ aGradientStops.erase( aWidestSegmentStart );
+
+ // Look for which is widest now
+ aIt = boost::next(aGradientStops.begin());
+ nWidestWidth = -1;
+ while( aIt != aGradientStops.end() )
+ {
+ if( aIt->first - boost::prior(aIt)->first > nWidestWidth )
+ {
+ nWidestWidth = aIt->first - boost::prior(aIt)->first;
+ aWidestSegmentStart = boost::prior(aIt);
+ }
+ aIt++;
+ }
+ }
+ SAL_INFO("oox.drawingml.gradient", "widest segment start: " << aWidestSegmentStart->first << ", border: " << nBorder);
+ assert( (!bSymmetric && !bSwap) || !(bSymmetric && bSwap) );
+
+ // Now we have a potential border and a largest segment. Use those.
+
+ aGradient.Style = bSymmetric ? awt::GradientStyle_AXIAL : awt::GradientStyle_LINEAR;
+ nDmlAngle = maGradientProps.moShadeAngle.get( 0 ) - nShapeRotation;
+ // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
+ aGradient.Angle = static_cast< sal_Int16 >( (4500 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 );
+ Color aStartColor, aEndColor;
+ if( bSymmetric )
+ {
+ aStartColor = boost::next(aWidestSegmentStart)->second;
+ aEndColor = aWidestSegmentStart->second;
+ nBorder *= 2;
+ }
+ else if( bSwap )
+ {
+ aStartColor = boost::next(aWidestSegmentStart)->second;
+ aEndColor = aWidestSegmentStart->second;
}
+ else
+ {
+ aStartColor = aWidestSegmentStart->second;
+ aEndColor = boost::next(aWidestSegmentStart)->second;
+ }
+
+ SAL_INFO("oox.drawingml.gradient", "start color: " << std::hex << aStartColor.getColor( rGraphicHelper, nPhClr ) << std::dec <<
+ "@" << (100-aStartColor.getTransparency()) << "%"
+ ", end color: " << std::hex << aEndColor.getColor( rGraphicHelper, nPhClr ) << std::dec <<
+ "@" << (100-aEndColor.getTransparency()) << "%");
+
+ aGradient.StartColor = aStartColor.getColor( rGraphicHelper, nPhClr );
+ aGradient.EndColor = aEndColor.getColor( rGraphicHelper, nPhClr );
+
+ if( aStartColor.hasTransparency() )
+ nStartTrans = aStartColor.getTransparency()*255/100;
+ if( aEndColor.hasTransparency() )
+ nEndTrans = aEndColor.getTransparency()*255/100;
+
+ aGradient.Border = 100*nBorder;
}
// push gradient or named gradient to property map