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