1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "oox/drawingml/fillproperties.hxx"
25 
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/awt/Gradient.hpp>
29 #include <com/sun/star/text/GraphicCrop.hpp>
30 #include <com/sun/star/awt/Size.hpp>
31 #include <com/sun/star/drawing/BitmapMode.hpp>
32 #include <com/sun/star/drawing/ColorMode.hpp>
33 #include <com/sun/star/drawing/FillStyle.hpp>
34 #include <com/sun/star/drawing/RectanglePoint.hpp>
35 #include <com/sun/star/graphic/XGraphicTransformer.hpp>
36 #include "oox/helper/graphichelper.hxx"
37 #include "oox/drawingml/drawingmltypes.hxx"
38 #include "oox/drawingml/shapepropertymap.hxx"
39 #include "oox/token/tokens.hxx"
40 
41 using namespace ::com::sun::star;
42 using namespace ::com::sun::star::drawing;
43 using namespace ::com::sun::star::graphic;
44 
45 using ::rtl::OUString;
46 using ::com::sun::star::uno::Reference;
47 using ::com::sun::star::uno::Exception;
48 using ::com::sun::star::uno::UNO_QUERY;
49 using ::com::sun::star::uno::UNO_QUERY_THROW;
50 using ::com::sun::star::geometry::IntegerRectangle2D;
51 
52 namespace oox {
53 namespace drawingml {
54 
55 // ============================================================================
56 
57 namespace {
58 
lclGetBitmapMode(sal_Int32 nToken)59 BitmapMode lclGetBitmapMode( sal_Int32 nToken )
60 {
61     switch( nToken )
62     {
63         case XML_tile:      return BitmapMode_REPEAT;
64         case XML_stretch:   return BitmapMode_STRETCH;
65     }
66     return BitmapMode_NO_REPEAT;
67 }
68 
lclGetRectanglePoint(sal_Int32 nToken)69 RectanglePoint lclGetRectanglePoint( sal_Int32 nToken )
70 {
71     switch( nToken )
72     {
73         case XML_tl:    return RectanglePoint_LEFT_TOP;
74         case XML_t:     return RectanglePoint_MIDDLE_TOP;
75         case XML_tr:    return RectanglePoint_RIGHT_TOP;
76         case XML_l:     return RectanglePoint_LEFT_MIDDLE;
77         case XML_ctr:   return RectanglePoint_MIDDLE_MIDDLE;
78         case XML_r:     return RectanglePoint_RIGHT_MIDDLE;
79         case XML_bl:    return RectanglePoint_LEFT_BOTTOM;
80         case XML_b:     return RectanglePoint_MIDDLE_BOTTOM;
81         case XML_br:    return RectanglePoint_RIGHT_BOTTOM;
82     }
83     return RectanglePoint_LEFT_TOP;
84 }
85 
lclGetOriginalSize(const GraphicHelper & rGraphicHelper,const Reference<XGraphic> & rxGraphic)86 const awt::Size lclGetOriginalSize( const GraphicHelper& rGraphicHelper, const Reference< XGraphic >& rxGraphic )
87 {
88     awt::Size aSizeHmm( 0, 0 );
89     try
90     {
91         Reference< beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
92         if( xGraphicPropertySet->getPropertyValue( CREATE_OUSTRING( "Size100thMM" ) ) >>= aSizeHmm )
93         {
94             if( !aSizeHmm.Width && !aSizeHmm.Height )
95             {   // MAPMODE_PIXEL USED :-(
96                 awt::Size aSourceSizePixel( 0, 0 );
97                 if( xGraphicPropertySet->getPropertyValue( CREATE_OUSTRING( "SizePixel" ) ) >>= aSourceSizePixel )
98                     aSizeHmm = rGraphicHelper.convertScreenPixelToHmm( aSourceSizePixel );
99             }
100         }
101     }
102     catch( Exception& )
103     {
104     }
105     return aSizeHmm;
106 }
107 
108 } // namespace
109 
110 // ============================================================================
111 
assignUsed(const GradientFillProperties & rSourceProps)112 void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
113 {
114     if( !rSourceProps.maGradientStops.empty() )
115         maGradientStops = rSourceProps.maGradientStops;
116     moFillToRect.assignIfUsed( rSourceProps.moFillToRect );
117     moTileRect.assignIfUsed( rSourceProps.moTileRect );
118     moGradientPath.assignIfUsed( rSourceProps.moGradientPath );
119     moShadeAngle.assignIfUsed( rSourceProps.moShadeAngle );
120     moShadeFlip.assignIfUsed( rSourceProps.moShadeFlip );
121     moShadeScaled.assignIfUsed( rSourceProps.moShadeScaled );
122     moRotateWithShape.assignIfUsed( rSourceProps.moRotateWithShape );
123 }
124 
125 // ============================================================================
126 
assignUsed(const PatternFillProperties & rSourceProps)127 void PatternFillProperties::assignUsed( const PatternFillProperties& rSourceProps )
128 {
129     maPattFgColor.assignIfUsed( rSourceProps.maPattFgColor );
130     maPattBgColor.assignIfUsed( rSourceProps.maPattBgColor );
131     moPattPreset.assignIfUsed( rSourceProps.moPattPreset );
132 }
133 
134 // ============================================================================
135 
assignUsed(const BlipFillProperties & rSourceProps)136 void BlipFillProperties::assignUsed( const BlipFillProperties& rSourceProps )
137 {
138     if( rSourceProps.mxGraphic.is() )
139         mxGraphic = rSourceProps.mxGraphic;
140     moBitmapMode.assignIfUsed( rSourceProps.moBitmapMode );
141     moFillRect.assignIfUsed( rSourceProps.moFillRect );
142     moTileOffsetX.assignIfUsed( rSourceProps.moTileOffsetX );
143     moTileOffsetY.assignIfUsed( rSourceProps.moTileOffsetY );
144     moTileScaleX.assignIfUsed( rSourceProps.moTileScaleX );
145     moTileScaleY.assignIfUsed( rSourceProps.moTileScaleY );
146     moTileAlign.assignIfUsed( rSourceProps.moTileAlign );
147     moTileFlip.assignIfUsed( rSourceProps.moTileFlip );
148     moRotateWithShape.assignIfUsed( rSourceProps.moRotateWithShape );
149     moColorEffect.assignIfUsed( rSourceProps.moColorEffect );
150     moBrightness.assignIfUsed( rSourceProps.moBrightness );
151     moContrast.assignIfUsed( rSourceProps.moContrast );
152     maColorChangeFrom.assignIfUsed( rSourceProps.maColorChangeFrom );
153     maColorChangeTo.assignIfUsed( rSourceProps.maColorChangeTo );
154 }
155 
156 // ============================================================================
157 
assignUsed(const FillProperties & rSourceProps)158 void FillProperties::assignUsed( const FillProperties& rSourceProps )
159 {
160     moFillType.assignIfUsed( rSourceProps.moFillType );
161     maFillColor.assignIfUsed( rSourceProps.maFillColor );
162     maGradientProps.assignUsed( rSourceProps.maGradientProps );
163     maPatternProps.assignUsed( rSourceProps.maPatternProps );
164     maBlipProps.assignUsed( rSourceProps.maBlipProps );
165 }
166 
getBestSolidColor() const167 Color FillProperties::getBestSolidColor() const
168 {
169     Color aSolidColor;
170     if( moFillType.has() ) switch( moFillType.get() )
171     {
172         case XML_solidFill:
173             aSolidColor = maFillColor;
174         break;
175         case XML_gradFill:
176             if( !maGradientProps.maGradientStops.empty() )
177                 aSolidColor = maGradientProps.maGradientStops.begin()->second;
178         break;
179         case XML_pattFill:
180             aSolidColor = maPatternProps.maPattBgColor.isUsed() ? maPatternProps.maPattBgColor : maPatternProps.maPattFgColor;
181         break;
182     }
183     return aSolidColor;
184 }
185 
pushToPropMap(ShapePropertyMap & rPropMap,const GraphicHelper & rGraphicHelper,sal_Int32 nShapeRotation,sal_Int32 nPhClr) const186 void FillProperties::pushToPropMap( ShapePropertyMap& rPropMap,
187         const GraphicHelper& rGraphicHelper, sal_Int32 nShapeRotation, sal_Int32 nPhClr ) const
188 {
189     if( moFillType.has() )
190     {
191         FillStyle eFillStyle = FillStyle_NONE;
192         switch( moFillType.get() )
193         {
194             case XML_noFill:
195                 eFillStyle = FillStyle_NONE;
196             break;
197 
198             case XML_solidFill:
199                 if( maFillColor.isUsed() )
200                 {
201                     rPropMap.setProperty( SHAPEPROP_FillColor, maFillColor.getColor( rGraphicHelper, nPhClr ) );
202                     if( maFillColor.hasTransparency() )
203                         rPropMap.setProperty( SHAPEPROP_FillTransparency, maFillColor.getTransparency() );
204                     eFillStyle = FillStyle_SOLID;
205                 }
206             break;
207 
208             case XML_gradFill:
209                 // do not create gradient struct if property is not supported...
210                 if( rPropMap.supportsProperty( SHAPEPROP_FillGradient ) )
211                 {
212                     awt::Gradient aGradient;
213                     aGradient.Angle = 900;
214                     aGradient.StartIntensity = 100;
215                     aGradient.EndIntensity = 100;
216 
217                     size_t nColorCount = maGradientProps.maGradientStops.size();
218                     if( nColorCount > 1 )
219                     {
220                         aGradient.StartColor = maGradientProps.maGradientStops.begin()->second.getColor( rGraphicHelper, nPhClr );
221                         aGradient.EndColor = maGradientProps.maGradientStops.rbegin()->second.getColor( rGraphicHelper, nPhClr );
222                     }
223 
224                     // "rotate with shape" not set, or set to false -> do not rotate
225                     if ( !maGradientProps.moRotateWithShape.get( false ) )
226                         nShapeRotation = 0;
227 
228                     sal_Int32 nDmlAngle = 0;
229                     if( maGradientProps.moGradientPath.has() )
230                     {
231                         aGradient.Style = (maGradientProps.moGradientPath.get() == XML_circle) ? awt::GradientStyle_ELLIPTICAL : awt::GradientStyle_RECT;
232                         // position of gradient center (limited to [30%;70%], otherwise gradient is too hidden)
233                         IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.get( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
234                         sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
235                         aGradient.XOffset = getLimitedValue< sal_Int16, sal_Int32 >( nCenterX / PER_PERCENT, 30, 70 );
236                         sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
237                         aGradient.YOffset = getLimitedValue< sal_Int16, sal_Int32 >( nCenterY / PER_PERCENT, 30, 70 );
238                         ::std::swap( aGradient.StartColor, aGradient.EndColor );
239                         nDmlAngle = nShapeRotation;
240                     }
241                     else
242                     {
243                         /*  Try to detect a VML axial gradient. This type of
244                             gradient is simulated by a 3-point linear gradient
245                             with equal start and end color. */
246                         bool bAxial = (nColorCount == 3) && (aGradient.StartColor == aGradient.EndColor);
247                         aGradient.Style = bAxial ? awt::GradientStyle_AXIAL : awt::GradientStyle_LINEAR;
248                         if( bAxial )
249                         {
250                             GradientFillProperties::GradientStopMap::const_iterator aIt = maGradientProps.maGradientStops.begin();
251                             // API StartColor is inner color in axial gradient
252                             aGradient.StartColor = (++aIt)->second.getColor( rGraphicHelper, nPhClr );
253                         }
254                         nDmlAngle = maGradientProps.moShadeAngle.get( 0 ) - nShapeRotation;
255                     }
256                     // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
257                     aGradient.Angle = static_cast< sal_Int16 >( (4500 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 );
258 
259                     // push gradient or named gradient to property map
260                     if( rPropMap.setProperty( SHAPEPROP_FillGradient, aGradient ) )
261                         eFillStyle = FillStyle_GRADIENT;
262                 }
263             break;
264 
265             case XML_blipFill:
266                 // do not start complex graphic transformation if property is not supported...
267                 if( maBlipProps.mxGraphic.is() && rPropMap.supportsProperty( SHAPEPROP_FillBitmapUrl ) )
268                 {
269                     // TODO: "rotate with shape" is not possible with our current core
270 
271                     OUString aGraphicUrl = rGraphicHelper.createGraphicObject( maBlipProps.mxGraphic );
272                     // push bitmap or named bitmap to property map
273                     if( (aGraphicUrl.getLength() > 0) && rPropMap.setProperty( SHAPEPROP_FillBitmapUrl, aGraphicUrl ) )
274                         eFillStyle = FillStyle_BITMAP;
275 
276                     // set other bitmap properties, if bitmap has been inserted into the map
277                     if( eFillStyle == FillStyle_BITMAP )
278                     {
279                         // bitmap mode (single, repeat, stretch)
280                         BitmapMode eBitmapMode = lclGetBitmapMode( maBlipProps.moBitmapMode.get( XML_TOKEN_INVALID ) );
281                         rPropMap.setProperty( SHAPEPROP_FillBitmapMode, eBitmapMode );
282 
283                         // additional settings for repeated bitmap
284                         if( eBitmapMode == BitmapMode_REPEAT )
285                         {
286                             // anchor position inside bitmap
287                             RectanglePoint eRectPoint = lclGetRectanglePoint( maBlipProps.moTileAlign.get( XML_tl ) );
288                             rPropMap.setProperty( SHAPEPROP_FillBitmapRectanglePoint, eRectPoint );
289 
290                             awt::Size aOriginalSize = lclGetOriginalSize( rGraphicHelper, maBlipProps.mxGraphic );
291                             if( (aOriginalSize.Width > 0) && (aOriginalSize.Height > 0) )
292                             {
293                                 // size of one bitmap tile (given as 1/1000 percent of bitmap size), convert to 1/100 mm
294                                 double fScaleX = maBlipProps.moTileScaleX.get( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
295                                 sal_Int32 nFillBmpSizeX = getLimitedValue< sal_Int32, double >( aOriginalSize.Width * fScaleX, 1, SAL_MAX_INT32 );
296                                 rPropMap.setProperty( SHAPEPROP_FillBitmapSizeX, nFillBmpSizeX );
297                                 double fScaleY = maBlipProps.moTileScaleY.get( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
298                                 sal_Int32 nFillBmpSizeY = getLimitedValue< sal_Int32, double >( aOriginalSize.Height * fScaleY, 1, SAL_MAX_INT32 );
299                                 rPropMap.setProperty( SHAPEPROP_FillBitmapSizeY, nFillBmpSizeY );
300 
301                                 // offset of the first bitmap tile (given as EMUs), convert to percent
302                                 sal_Int16 nTileOffsetX = getDoubleIntervalValue< sal_Int16 >( maBlipProps.moTileOffsetX.get( 0 ) / 3.6 / aOriginalSize.Width, 0, 100 );
303                                 rPropMap.setProperty( SHAPEPROP_FillBitmapOffsetX, nTileOffsetX );
304                                 sal_Int16 nTileOffsetY = getDoubleIntervalValue< sal_Int16 >( maBlipProps.moTileOffsetY.get( 0 ) / 3.6 / aOriginalSize.Height, 0, 100 );
305                                 rPropMap.setProperty( SHAPEPROP_FillBitmapOffsetY, nTileOffsetY );
306                             }
307                         }
308                     }
309                 }
310             break;
311 
312             case XML_pattFill:
313             {
314                 // todo
315                 Color aColor = getBestSolidColor();
316                 if( aColor.isUsed() )
317                 {
318                     rPropMap.setProperty( SHAPEPROP_FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
319                     if( aColor.hasTransparency() )
320                         rPropMap.setProperty( SHAPEPROP_FillTransparency, aColor.getTransparency() );
321                     eFillStyle = FillStyle_SOLID;
322                 }
323             }
324             break;
325 
326             case XML_grpFill:
327                 // todo
328                 eFillStyle = FillStyle_NONE;
329             break;
330         }
331 
332         // set final fill style property
333         rPropMap.setProperty( SHAPEPROP_FillStyle, eFillStyle );
334     }
335 }
336 
337 // ============================================================================
338 
assignUsed(const GraphicProperties & rSourceProps)339 void GraphicProperties::assignUsed( const GraphicProperties& rSourceProps )
340 {
341     maBlipProps.assignUsed( rSourceProps.maBlipProps );
342 }
343 
pushToPropMap(PropertyMap & rPropMap,const GraphicHelper & rGraphicHelper,sal_Int32 nPhClr) const344 void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelper& rGraphicHelper, sal_Int32 nPhClr ) const
345 {
346     if( maBlipProps.mxGraphic.is() )
347     {
348         // created transformed graphic
349         Reference< XGraphic > xGraphic = maBlipProps.mxGraphic;
350         if( maBlipProps.maColorChangeFrom.isUsed() && maBlipProps.maColorChangeTo.isUsed() )
351         {
352             sal_Int32 nFromColor = maBlipProps.maColorChangeFrom.getColor( rGraphicHelper, nPhClr );
353             sal_Int32 nToColor = maBlipProps.maColorChangeTo.getColor( rGraphicHelper, nPhClr );
354             if ( (nFromColor != nToColor) || maBlipProps.maColorChangeTo.hasTransparency() ) try
355             {
356                 sal_Int16 nToTransparence = maBlipProps.maColorChangeTo.getTransparency();
357                 sal_Int8 nToAlpha = static_cast< sal_Int8 >( (100 - nToTransparence) / 39.062 );   // ?!? correct ?!?
358                 Reference< XGraphicTransformer > xTransformer( maBlipProps.mxGraphic, UNO_QUERY_THROW );
359                 xGraphic = xTransformer->colorChange( maBlipProps.mxGraphic, nFromColor, 9, nToColor, nToAlpha );
360             }
361             catch( Exception& )
362             {
363             }
364         }
365 
366         OUString aGraphicUrl = rGraphicHelper.createGraphicObject( xGraphic );
367         if( aGraphicUrl.getLength() > 0 )
368             rPropMap[ PROP_GraphicURL ] <<= aGraphicUrl;
369 
370 		// cropping
371 		if ( maBlipProps.moClipRect.has() )
372 		{
373 			geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.get() );
374 			awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
375 			if ( aOriginalSize.Width && aOriginalSize.Height )
376 			{
377 				text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
378 				if ( oClipRect.X1 )
379 					aGraphCrop.Left = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X1 ) / 100000 );
380 				if ( oClipRect.Y1 )
381 					aGraphCrop.Top = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y1 ) / 100000 );
382 				if ( oClipRect.X2 )
383 					aGraphCrop.Right = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X2 ) / 100000 );
384 				if ( oClipRect.Y2 )
385 					aGraphCrop.Bottom = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y2 ) / 100000 );
386 				rPropMap[ PROP_GraphicCrop ] <<= aGraphCrop;
387 			}
388 		}
389     }
390 
391     // color effect
392     ColorMode eColorMode = ColorMode_STANDARD;
393     switch( maBlipProps.moColorEffect.get( XML_TOKEN_INVALID ) )
394     {
395         case XML_biLevel:   eColorMode = ColorMode_MONO;    break;
396         case XML_grayscl:   eColorMode = ColorMode_GREYS;   break;
397     }
398     rPropMap[ PROP_GraphicColorMode ] <<= eColorMode;
399 
400     // brightness and contrast
401     sal_Int16 nBrightness = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moBrightness.get( 0 ) / PER_PERCENT, -100, 100 );
402     if( nBrightness != 0 )
403         rPropMap[ PROP_AdjustLuminance ] <<= nBrightness;
404     sal_Int16 nContrast = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moContrast.get( 0 ) / PER_PERCENT, -100, 100 );
405     if( nContrast != 0 )
406         rPropMap[ PROP_AdjustContrast ] <<= nContrast;
407 }
408 
409 // ============================================================================
410 
411 } // namespace drawingml
412 } // namespace oox
413 
414