|  | /************************************************************** | 
|  | * | 
|  | * Licensed to the Apache Software Foundation (ASF) under one | 
|  | * or more contributor license agreements.  See the NOTICE file | 
|  | * distributed with this work for additional information | 
|  | * regarding copyright ownership.  The ASF licenses this file | 
|  | * to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance | 
|  | * with the License.  You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, | 
|  | * software distributed under the License is distributed on an | 
|  | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | * KIND, either express or implied.  See the License for the | 
|  | * specific language governing permissions and limitations | 
|  | * under the License. | 
|  | * | 
|  | *************************************************************/ | 
|  |  | 
|  |  | 
|  |  | 
|  | // MARKER(update_precomp.py): autogen include statement, do not remove | 
|  | #include "precompiled_vcl.hxx" | 
|  |  | 
|  | #include <functional> | 
|  | #include <algorithm> | 
|  | #include <utility> | 
|  | #include <list> | 
|  | #include <vector> | 
|  |  | 
|  | #include <basegfx/polygon/b2dpolygon.hxx> | 
|  | #include <basegfx/polygon/b2dpolygontools.hxx> | 
|  |  | 
|  | #include <tools/debug.hxx> | 
|  |  | 
|  | #include <vcl/virdev.hxx> | 
|  | #include <vcl/metaact.hxx> | 
|  | #include <vcl/gdimtf.hxx> | 
|  | #include <vcl/salbtype.hxx> | 
|  | #include <vcl/print.hxx> | 
|  | #include <vcl/svapp.hxx> | 
|  | #include <vcl/bmpacc.hxx> | 
|  |  | 
|  | #include <print.h> | 
|  |  | 
|  | #include "pdfwriter_impl.hxx" | 
|  |  | 
|  | // ----------- | 
|  | // - Defines - | 
|  | // ----------- | 
|  |  | 
|  | #define MAX_TILE_WIDTH  1024 | 
|  | #define MAX_TILE_HEIGHT 1024 | 
|  |  | 
|  | // --------- | 
|  | // - Types - | 
|  | // --------- | 
|  |  | 
|  | typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile | 
|  |  | 
|  | typedef ::std::list< Component > ComponentList; | 
|  |  | 
|  | // List of (intersecting) actions, plus overall bounds | 
|  | struct ConnectedComponents | 
|  | { | 
|  | ConnectedComponents() : | 
|  | aComponentList(), | 
|  | aBounds(), | 
|  | aBgColor(COL_WHITE), | 
|  | bIsSpecial(false), | 
|  | bIsFullyTransparent(false) | 
|  | {} | 
|  |  | 
|  | ComponentList	aComponentList; | 
|  | Rectangle		aBounds; | 
|  | Color           aBgColor; | 
|  | bool			bIsSpecial; | 
|  | bool			bIsFullyTransparent; | 
|  | }; | 
|  |  | 
|  | typedef ::std::list< ConnectedComponents > ConnectedComponentsList; | 
|  |  | 
|  |  | 
|  | // ----------- | 
|  | // - Printer - | 
|  | // ----------- | 
|  |  | 
|  | /** #i10613# Extracted from Printer::GetPreparedMetaFile. Returns true | 
|  | if given action requires special handling (usually because of | 
|  | transparency) | 
|  | */ | 
|  | static bool ImplIsActionSpecial( const MetaAction& rAct ) | 
|  | { | 
|  | switch( rAct.GetType() ) | 
|  | { | 
|  | case META_TRANSPARENT_ACTION: | 
|  | return true; | 
|  |  | 
|  | case META_FLOATTRANSPARENT_ACTION: | 
|  | return true; | 
|  |  | 
|  | case META_BMPEX_ACTION: | 
|  | return static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().IsTransparent(); | 
|  |  | 
|  | case META_BMPEXSCALE_ACTION: | 
|  | return static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx().IsTransparent(); | 
|  |  | 
|  | case META_BMPEXSCALEPART_ACTION: | 
|  | return static_cast<const MetaBmpExScalePartAction&>(rAct).GetBitmapEx().IsTransparent(); | 
|  |  | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if | 
|  | yes, return true and update o_rBgColor | 
|  | */ | 
|  | static bool checkRect( Rectangle&       io_rPrevRect, | 
|  | Color&           o_rBgColor, | 
|  | const Rectangle& rCurrRect, | 
|  | OutputDevice&    rMapModeVDev ) | 
|  | { | 
|  | // shape needs to fully cover previous content, and have uniform | 
|  | // color | 
|  | const bool bRet( | 
|  | rMapModeVDev.LogicToPixel(rCurrRect).IsInside(io_rPrevRect) && | 
|  | rMapModeVDev.IsFillColor() ); | 
|  |  | 
|  | if( bRet ) | 
|  | { | 
|  | io_rPrevRect = rCurrRect; | 
|  | o_rBgColor = rMapModeVDev.GetFillColor(); | 
|  | } | 
|  |  | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | /** #107169# Convert BitmapEx to Bitmap with appropriately blended | 
|  | color. Convert MetaTransparentAction to plain polygon, | 
|  | appropriately colored | 
|  |  | 
|  | @param o_rMtf | 
|  | Add converted actions to this metafile | 
|  | */ | 
|  | static void ImplConvertTransparentAction( GDIMetaFile&        o_rMtf, | 
|  | const MetaAction&   rAct, | 
|  | const OutputDevice& rStateOutDev, | 
|  | Color               aBgColor ) | 
|  | { | 
|  | if( rAct.GetType() == META_TRANSPARENT_ACTION ) | 
|  | { | 
|  | const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct); | 
|  | sal_uInt16				         nTransparency( pTransAct->GetTransparence() ); | 
|  |  | 
|  | // #i10613# Respect transparency for draw color | 
|  | if( nTransparency ) | 
|  | { | 
|  | o_rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR|PUSH_FILLCOLOR ) ); | 
|  |  | 
|  | // assume white background for alpha blending | 
|  | Color aLineColor( rStateOutDev.GetLineColor() ); | 
|  | aLineColor.SetRed( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetRed()) / 100L ) ); | 
|  | aLineColor.SetGreen( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetGreen()) / 100L ) ); | 
|  | aLineColor.SetBlue( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetBlue()) / 100L ) ); | 
|  | o_rMtf.AddAction( new MetaLineColorAction(aLineColor, sal_True) ); | 
|  |  | 
|  | Color aFillColor( rStateOutDev.GetFillColor() ); | 
|  | aFillColor.SetRed( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetRed()) / 100L ) ); | 
|  | aFillColor.SetGreen( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetGreen()) / 100L ) ); | 
|  | aFillColor.SetBlue( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetBlue()) / 100L ) ); | 
|  | o_rMtf.AddAction( new MetaFillColorAction(aFillColor, sal_True) ); | 
|  | } | 
|  |  | 
|  | o_rMtf.AddAction( new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()) ); | 
|  |  | 
|  | if( nTransparency ) | 
|  | o_rMtf.AddAction( new MetaPopAction() ); | 
|  | } | 
|  | else | 
|  | { | 
|  | BitmapEx aBmpEx; | 
|  |  | 
|  | switch( rAct.GetType() ) | 
|  | { | 
|  | case META_BMPEX_ACTION: | 
|  | aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx(); | 
|  | break; | 
|  |  | 
|  | case META_BMPEXSCALE_ACTION: | 
|  | aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx(); | 
|  | break; | 
|  |  | 
|  | case META_BMPEXSCALEPART_ACTION: | 
|  | aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx(); | 
|  | break; | 
|  |  | 
|  | case META_TRANSPARENT_ACTION: | 
|  |  | 
|  | default: | 
|  | DBG_ERROR("Printer::GetPreparedMetafile impossible state reached"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | Bitmap aBmp( aBmpEx.GetBitmap() ); | 
|  | if( !aBmpEx.IsAlpha() ) | 
|  | { | 
|  | // blend with mask | 
|  | BitmapReadAccess* pRA = aBmp.AcquireReadAccess(); | 
|  |  | 
|  | if( !pRA ) | 
|  | return; // what else should I do? | 
|  |  | 
|  | Color aActualColor( aBgColor ); | 
|  |  | 
|  | if( pRA->HasPalette() ) | 
|  | aActualColor = pRA->GetBestPaletteColor( aBgColor ).operator Color(); | 
|  |  | 
|  | aBmp.ReleaseAccess(pRA); | 
|  |  | 
|  | // did we get true white? | 
|  | if( aActualColor.GetColorError( aBgColor ) ) | 
|  | { | 
|  | // no, create truecolor bitmap, then | 
|  | aBmp.Convert( BMP_CONVERSION_24BIT ); | 
|  |  | 
|  | // fill masked out areas white | 
|  | aBmp.Replace( aBmpEx.GetMask(), aBgColor ); | 
|  | } | 
|  | else | 
|  | { | 
|  | // fill masked out areas white | 
|  | aBmp.Replace( aBmpEx.GetMask(), aActualColor ); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // blend with alpha channel | 
|  | aBmp.Convert( BMP_CONVERSION_24BIT ); | 
|  | aBmp.Blend(aBmpEx.GetAlpha(),aBgColor); | 
|  | } | 
|  |  | 
|  | // add corresponding action | 
|  | switch( rAct.GetType() ) | 
|  | { | 
|  | case META_BMPEX_ACTION: | 
|  | o_rMtf.AddAction( new MetaBmpAction( | 
|  | static_cast<const MetaBmpExAction&>(rAct).GetPoint(), | 
|  | aBmp )); | 
|  | break; | 
|  | case META_BMPEXSCALE_ACTION: | 
|  | o_rMtf.AddAction( new MetaBmpScaleAction( | 
|  | static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(), | 
|  | static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(), | 
|  | aBmp )); | 
|  | break; | 
|  | case META_BMPEXSCALEPART_ACTION: | 
|  | o_rMtf.AddAction( new MetaBmpScalePartAction( | 
|  | static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(), | 
|  | static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize(), | 
|  | static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcPoint(), | 
|  | static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcSize(), | 
|  | aBmp )); | 
|  | break; | 
|  | default: | 
|  | DBG_ERROR("Unexpected case"); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // #i10613# Extracted from ImplCheckRect::ImplCreate | 
|  | // Returns true, if given action creates visible (i.e. non-transparent) output | 
|  | static bool ImplIsNotTransparent( const MetaAction& rAct, const OutputDevice& rOut ) | 
|  | { | 
|  | const bool	bLineTransparency( rOut.IsLineColor() ? rOut.GetLineColor().GetTransparency() == 255 : true ); | 
|  | const bool 	bFillTransparency( rOut.IsFillColor() ? rOut.GetFillColor().GetTransparency() == 255 : true ); | 
|  | bool		bRet( false ); | 
|  |  | 
|  | switch( rAct.GetType() ) | 
|  | { | 
|  | case META_POINT_ACTION: | 
|  | if( !bLineTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_LINE_ACTION: | 
|  | if( !bLineTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_RECT_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_ROUNDRECT_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_ELLIPSE_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_ARC_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_PIE_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_CHORD_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_POLYLINE_ACTION: | 
|  | if( !bLineTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_POLYGON_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_POLYPOLYGON_ACTION: | 
|  | if( !bLineTransparency || !bFillTransparency ) | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | case META_TEXT_ACTION: | 
|  | { | 
|  | const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct); | 
|  | const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); | 
|  |  | 
|  | if( aString.Len() ) | 
|  | bRet = true; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case META_TEXTARRAY_ACTION: | 
|  | { | 
|  | const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct); | 
|  | const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); | 
|  |  | 
|  | if( aString.Len() ) | 
|  | bRet = true; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case META_PIXEL_ACTION: | 
|  | case META_BMP_ACTION: | 
|  | case META_BMPSCALE_ACTION: | 
|  | case META_BMPSCALEPART_ACTION: | 
|  | case META_BMPEX_ACTION: | 
|  | case META_BMPEXSCALE_ACTION: | 
|  | case META_BMPEXSCALEPART_ACTION: | 
|  | case META_MASK_ACTION: | 
|  | case META_MASKSCALE_ACTION: | 
|  | case META_MASKSCALEPART_ACTION: | 
|  | case META_GRADIENT_ACTION: | 
|  | case META_GRADIENTEX_ACTION: | 
|  | case META_HATCH_ACTION: | 
|  | case META_WALLPAPER_ACTION: | 
|  | case META_TRANSPARENT_ACTION: | 
|  | case META_FLOATTRANSPARENT_ACTION: | 
|  | case META_EPS_ACTION: | 
|  | case META_TEXTRECT_ACTION: | 
|  | case META_STRETCHTEXT_ACTION: | 
|  | case META_TEXTLINE_ACTION: | 
|  | // all other actions: generate non-transparent output | 
|  | bRet = true; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return bRet; | 
|  | } | 
|  |  | 
|  | // #i10613# Extracted from ImplCheckRect::ImplCreate | 
|  | static Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut ) | 
|  | { | 
|  | Rectangle aActionBounds; | 
|  |  | 
|  | switch( rAct.GetType() ) | 
|  | { | 
|  | case META_PIXEL_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) ); | 
|  | break; | 
|  |  | 
|  | case META_POINT_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) ); | 
|  | break; | 
|  |  | 
|  | case META_LINE_ACTION: | 
|  | { | 
|  | const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct); | 
|  | aActionBounds = Rectangle( rMetaLineAction.GetStartPoint(),  rMetaLineAction.GetEndPoint() ); | 
|  | aActionBounds.Justify(); | 
|  | const long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth()); | 
|  | if(nLineWidth) | 
|  | { | 
|  | const long nHalfLineWidth((nLineWidth + 1) / 2); | 
|  | aActionBounds.Left() -= nHalfLineWidth; | 
|  | aActionBounds.Top() -= nHalfLineWidth; | 
|  | aActionBounds.Right() += nHalfLineWidth; | 
|  | aActionBounds.Bottom() += nHalfLineWidth; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case META_RECT_ACTION: | 
|  | aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect(); | 
|  | break; | 
|  |  | 
|  | case META_ROUNDRECT_ACTION: | 
|  | aActionBounds = Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(), | 
|  | static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(), | 
|  | static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_ELLIPSE_ACTION: | 
|  | { | 
|  | const Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect(); | 
|  | aActionBounds = Polygon( rRect.Center(), | 
|  | rRect.GetWidth() >> 1, | 
|  | rRect.GetHeight() >> 1 ).GetBoundRect(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case META_ARC_ACTION: | 
|  | aActionBounds = Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(), | 
|  | static_cast<const MetaArcAction&>(rAct).GetStartPoint(), | 
|  | static_cast<const MetaArcAction&>(rAct).GetEndPoint(), POLY_ARC ).GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_PIE_ACTION: | 
|  | aActionBounds = Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(), | 
|  | static_cast<const MetaPieAction&>(rAct).GetStartPoint(), | 
|  | static_cast<const MetaPieAction&>(rAct).GetEndPoint(), POLY_PIE ).GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_CHORD_ACTION: | 
|  | aActionBounds = Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(), | 
|  | static_cast<const MetaChordAction&>(rAct).GetStartPoint(), | 
|  | static_cast<const MetaChordAction&>(rAct).GetEndPoint(), POLY_CHORD ).GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_POLYLINE_ACTION: | 
|  | { | 
|  | const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct); | 
|  | aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect(); | 
|  | const long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth()); | 
|  | if(nLineWidth) | 
|  | { | 
|  | const long nHalfLineWidth((nLineWidth + 1) / 2); | 
|  | aActionBounds.Left() -= nHalfLineWidth; | 
|  | aActionBounds.Top() -= nHalfLineWidth; | 
|  | aActionBounds.Right() += nHalfLineWidth; | 
|  | aActionBounds.Bottom() += nHalfLineWidth; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case META_POLYGON_ACTION: | 
|  | aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_POLYPOLYGON_ACTION: | 
|  | aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_BMP_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(), | 
|  | rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) ); | 
|  | break; | 
|  |  | 
|  | case META_BMPSCALE_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(), | 
|  | static_cast<const MetaBmpScaleAction&>(rAct).GetSize() ); | 
|  | break; | 
|  |  | 
|  | case META_BMPSCALEPART_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(), | 
|  | static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() ); | 
|  | break; | 
|  |  | 
|  | case META_BMPEX_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(), | 
|  | rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) ); | 
|  | break; | 
|  |  | 
|  | case META_BMPEXSCALE_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(), | 
|  | static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() ); | 
|  | break; | 
|  |  | 
|  | case META_BMPEXSCALEPART_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(), | 
|  | static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() ); | 
|  | break; | 
|  |  | 
|  | case META_MASK_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(), | 
|  | rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) ); | 
|  | break; | 
|  |  | 
|  | case META_MASKSCALE_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(), | 
|  | static_cast<const MetaMaskScaleAction&>(rAct).GetSize() ); | 
|  | break; | 
|  |  | 
|  | case META_MASKSCALEPART_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(), | 
|  | static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() ); | 
|  | break; | 
|  |  | 
|  | case META_GRADIENT_ACTION: | 
|  | aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect(); | 
|  | break; | 
|  |  | 
|  | case META_GRADIENTEX_ACTION: | 
|  | aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_HATCH_ACTION: | 
|  | aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_WALLPAPER_ACTION: | 
|  | aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect(); | 
|  | break; | 
|  |  | 
|  | case META_TRANSPARENT_ACTION: | 
|  | aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect(); | 
|  | break; | 
|  |  | 
|  | case META_FLOATTRANSPARENT_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(), | 
|  | static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() ); | 
|  | break; | 
|  |  | 
|  | case META_EPS_ACTION: | 
|  | aActionBounds = Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(), | 
|  | static_cast<const MetaEPSAction&>(rAct).GetSize() ); | 
|  | break; | 
|  |  | 
|  | case META_TEXT_ACTION: | 
|  | { | 
|  | const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct); | 
|  | const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); | 
|  |  | 
|  | if( aString.Len() ) | 
|  | { | 
|  | const Point aPtLog( rTextAct.GetPoint() ); | 
|  |  | 
|  | // #105987# Use API method instead of Impl* methods | 
|  | // #107490# Set base parameter equal to index parameter | 
|  | rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(), | 
|  | rTextAct.GetIndex(), rTextAct.GetLen() ); | 
|  | aActionBounds.Move( aPtLog.X(), aPtLog.Y() ); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case META_TEXTARRAY_ACTION: | 
|  | { | 
|  | const MetaTextArrayAction&	rTextAct = static_cast<const MetaTextArrayAction&>(rAct); | 
|  | const XubString 			aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); | 
|  | const long              	nLen = aString.Len(); | 
|  |  | 
|  | if( nLen ) | 
|  | { | 
|  | // #105987# ImplLayout takes everything in logical coordinates | 
|  | SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(), | 
|  | rTextAct.GetLen(), rTextAct.GetPoint(), | 
|  | 0, rTextAct.GetDXArray() ); | 
|  | if( pSalLayout ) | 
|  | { | 
|  | Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) ); | 
|  | aActionBounds = rOut.PixelToLogic( aBoundRect ); | 
|  | pSalLayout->Release(); | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case META_TEXTRECT_ACTION: | 
|  | aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect(); | 
|  | break; | 
|  |  | 
|  | case META_STRETCHTEXT_ACTION: | 
|  | { | 
|  | const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct); | 
|  | const XubString 			 aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); | 
|  | const long              	 nLen = aString.Len(); | 
|  |  | 
|  | // #i16195# Literate copy from TextArray action, the | 
|  | // semantics for the ImplLayout call are copied from the | 
|  | // OutDev::DrawStretchText() code. Unfortunately, also in | 
|  | // this case, public outdev methods such as GetTextWidth() | 
|  | // don't provide enough info. | 
|  | if( nLen ) | 
|  | { | 
|  | // #105987# ImplLayout takes everything in logical coordinates | 
|  | SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(), | 
|  | rTextAct.GetLen(), rTextAct.GetPoint(), | 
|  | rTextAct.GetWidth() ); | 
|  | if( pSalLayout ) | 
|  | { | 
|  | Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) ); | 
|  | aActionBounds = rOut.PixelToLogic( aBoundRect ); | 
|  | pSalLayout->Release(); | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case META_TEXTLINE_ACTION: | 
|  | DBG_ERROR("META_TEXTLINE_ACTION not supported"); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if( !aActionBounds.IsEmpty() ) | 
|  | return rOut.LogicToPixel( aActionBounds ); | 
|  | else | 
|  | return Rectangle(); | 
|  | } | 
|  |  | 
|  | static bool ImplIsActionHandlingTransparency( const MetaAction& rAct ) | 
|  | { | 
|  | // META_FLOATTRANSPARENT_ACTION can contain a whole metafile, | 
|  | // which is to be rendered with the given transparent gradient. We | 
|  | // currently cannot emulate transparent painting on a white | 
|  | // background reliably. | 
|  |  | 
|  | // the remainder can handle printing itself correctly on a uniform | 
|  | // white background. | 
|  | switch( rAct.GetType() ) | 
|  | { | 
|  | case META_TRANSPARENT_ACTION: | 
|  | case META_BMPEX_ACTION: | 
|  | case META_BMPEXSCALE_ACTION: | 
|  | case META_BMPEXSCALEPART_ACTION: | 
|  | return true; | 
|  |  | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // remove comment to enable highlighting of generated output | 
|  | bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf, | 
|  | long nMaxBmpDPIX, long nMaxBmpDPIY, | 
|  | bool bReduceTransparency, bool bTransparencyAutoMode, | 
|  | bool bDownsampleBitmaps, | 
|  | const Color& rBackground | 
|  | ) | 
|  | { | 
|  | MetaAction*             pCurrAct; | 
|  | bool		            bTransparent( false ); | 
|  |  | 
|  | rOutMtf.Clear(); | 
|  |  | 
|  | if( ! bReduceTransparency || bTransparencyAutoMode ) | 
|  | { | 
|  | // watch for transparent drawing actions | 
|  | for( pCurrAct = ( (GDIMetaFile&) rInMtf ).FirstAction(); | 
|  | pCurrAct && !bTransparent; | 
|  | pCurrAct = ( (GDIMetaFile&) rInMtf ).NextAction() ) | 
|  | { | 
|  | // #i10613# Extracted "specialness" predicate into extra method | 
|  |  | 
|  | // #107169# Also examine metafiles with masked bitmaps in | 
|  | // detail. Further down, this is optimized in such a way | 
|  | // that there's no unnecessary painting of masked bitmaps | 
|  | // (which are _always_ subdivided into rectangular regions | 
|  | // of uniform opacity): if a masked bitmap is printed over | 
|  | // empty background, we convert to a plain bitmap with | 
|  | // white background. | 
|  | if( ImplIsActionSpecial( *pCurrAct ) ) | 
|  | { | 
|  | bTransparent = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // #i10613# Determine set of connected components containing transparent objects. These are | 
|  | // then processed as bitmaps, the original actions are removed from the metafile. | 
|  | if( !bTransparent ) | 
|  | { | 
|  | // nothing transparent -> just copy | 
|  | rOutMtf = rInMtf; | 
|  | } | 
|  | else | 
|  | { | 
|  | // #i10613# | 
|  | // This works as follows: we want a number of distinct sets of | 
|  | // connected components, where each set contains metafile | 
|  | // actions that are intersecting (note: there are possibly | 
|  | // more actions contained as are directly intersecting, | 
|  | // because we can only produce rectangular bitmaps later | 
|  | // on. Thus, each set of connected components is the smallest | 
|  | // enclosing, axis-aligned rectangle that completely bounds a | 
|  | // number of intersecting metafile actions, plus any action | 
|  | // that would otherwise be cut in two). Therefore, we | 
|  | // iteratively add metafile actions from the original metafile | 
|  | // to this connected components list (aCCList), by checking | 
|  | // each element's bounding box against intersection with the | 
|  | // metaaction at hand. | 
|  | // All those intersecting elements are removed from aCCList | 
|  | // and collected in a temporary list (aCCMergeList). After all | 
|  | // elements have been checked, the aCCMergeList elements are | 
|  | // merged with the metaaction at hand into one resulting | 
|  | // connected component, with one big bounding box, and | 
|  | // inserted into aCCList again. | 
|  | // The time complexity of this algorithm is O(n^3), where n is | 
|  | // the number of metafile actions, and it finds all distinct | 
|  | // regions of rectangle-bounded connected components. This | 
|  | // algorithm was designed by AF. | 
|  | // | 
|  |  | 
|  | // | 
|  | //  STAGE 1: Detect background | 
|  | //  ========================== | 
|  | // | 
|  |  | 
|  | // Receives uniform background content, and is _not_ merged | 
|  | // nor checked for intersection against other aCCList elements | 
|  | ConnectedComponents aBackgroundComponent; | 
|  |  | 
|  | // create an OutputDevice to record mapmode changes and the like | 
|  | VirtualDevice aMapModeVDev; | 
|  | aMapModeVDev.mnDPIX = mnDPIX; | 
|  | aMapModeVDev.mnDPIY = mnDPIY; | 
|  | aMapModeVDev.EnableOutput(sal_False); | 
|  |  | 
|  | int nLastBgAction, nActionNum; | 
|  |  | 
|  | // weed out page-filling background objects (if they are | 
|  | // uniformly coloured). Keeping them outside the other | 
|  | // connected components often prevents whole-page bitmap | 
|  | // generation. | 
|  | bool bStillBackground=true; // true until first non-bg action | 
|  | nActionNum=0; nLastBgAction=-1; | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(); | 
|  | if( rBackground != Color( COL_TRANSPARENT ) ) | 
|  | { | 
|  | aBackgroundComponent.aBgColor = rBackground; | 
|  | if( meOutDevType == OUTDEV_PRINTER ) | 
|  | { | 
|  | Printer* pThis = dynamic_cast<Printer*>(this); | 
|  | Point aPageOffset = pThis->GetPageOffsetPixel(); | 
|  | aPageOffset = Point( 0, 0 ) - aPageOffset; | 
|  | Size aSize  = pThis->GetPaperSizePixel(); | 
|  | aBackgroundComponent.aBounds = Rectangle( aPageOffset, aSize ); | 
|  | } | 
|  | else | 
|  | aBackgroundComponent.aBounds = Rectangle( Point( 0, 0 ), GetOutputSizePixel() ); | 
|  | } | 
|  | while( pCurrAct && bStillBackground ) | 
|  | { | 
|  | switch( pCurrAct->GetType() ) | 
|  | { | 
|  | case META_RECT_ACTION: | 
|  | { | 
|  | if( !checkRect( | 
|  | aBackgroundComponent.aBounds, | 
|  | aBackgroundComponent.aBgColor, | 
|  | static_cast<const MetaRectAction*>(pCurrAct)->GetRect(), | 
|  | aMapModeVDev) ) | 
|  | bStillBackground=false; // incomplete occlusion of background | 
|  | else | 
|  | nLastBgAction=nActionNum; // this _is_ background | 
|  | break; | 
|  | } | 
|  | case META_POLYGON_ACTION: | 
|  | { | 
|  | const Polygon aPoly( | 
|  | static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon()); | 
|  | if( !basegfx::tools::isRectangle( | 
|  | aPoly.getB2DPolygon()) || | 
|  | !checkRect( | 
|  | aBackgroundComponent.aBounds, | 
|  | aBackgroundComponent.aBgColor, | 
|  | aPoly.GetBoundRect(), | 
|  | aMapModeVDev) ) | 
|  | bStillBackground=false; // incomplete occlusion of background | 
|  | else | 
|  | nLastBgAction=nActionNum; // this _is_ background | 
|  | break; | 
|  | } | 
|  | case META_POLYPOLYGON_ACTION: | 
|  | { | 
|  | const PolyPolygon aPoly( | 
|  | static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon()); | 
|  | if( aPoly.Count() != 1 || | 
|  | !basegfx::tools::isRectangle( | 
|  | aPoly[0].getB2DPolygon()) || | 
|  | !checkRect( | 
|  | aBackgroundComponent.aBounds, | 
|  | aBackgroundComponent.aBgColor, | 
|  | aPoly.GetBoundRect(), | 
|  | aMapModeVDev) ) | 
|  | bStillBackground=false; // incomplete occlusion of background | 
|  | else | 
|  | nLastBgAction=nActionNum; // this _is_ background | 
|  | break; | 
|  | } | 
|  | case META_WALLPAPER_ACTION: | 
|  | { | 
|  | if( !checkRect( | 
|  | aBackgroundComponent.aBounds, | 
|  | aBackgroundComponent.aBgColor, | 
|  | static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(), | 
|  | aMapModeVDev) ) | 
|  | bStillBackground=false; // incomplete occlusion of background | 
|  | else | 
|  | nLastBgAction=nActionNum; // this _is_ background | 
|  | break; | 
|  | } | 
|  | default: | 
|  | { | 
|  | if( ImplIsNotTransparent( *pCurrAct, | 
|  | aMapModeVDev ) ) | 
|  | bStillBackground=false; // non-transparent action, possibly | 
|  | // not uniform | 
|  | else | 
|  | // extend current bounds (next uniform action | 
|  | // needs to fully cover this area) | 
|  | aBackgroundComponent.aBounds.Union( | 
|  | ImplCalcActionBounds(*pCurrAct, aMapModeVDev) ); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // execute action to get correct MapModes etc. | 
|  | pCurrAct->Execute( &aMapModeVDev ); | 
|  |  | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(); | 
|  | ++nActionNum; | 
|  | } | 
|  |  | 
|  | // clean up aMapModeVDev | 
|  | sal_uInt32 nCount = aMapModeVDev.GetGCStackDepth(); | 
|  | while( nCount-- ) | 
|  | aMapModeVDev.Pop(); | 
|  |  | 
|  | ConnectedComponentsList	aCCList; // list containing distinct sets of connected components as elements. | 
|  |  | 
|  | // fast-forward until one after the last background action | 
|  | // (need to reconstruct map mode vdev state) | 
|  | nActionNum=0; | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(); | 
|  | while( pCurrAct && nActionNum<=nLastBgAction ) | 
|  | { | 
|  | // up to and including last ink-generating background | 
|  | // action go to background component | 
|  | aBackgroundComponent.aComponentList.push_back( | 
|  | ::std::make_pair( | 
|  | pCurrAct, nActionNum) ); | 
|  |  | 
|  | // execute action to get correct MapModes etc. | 
|  | pCurrAct->Execute( &aMapModeVDev ); | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(); | 
|  | ++nActionNum; | 
|  | } | 
|  |  | 
|  | // | 
|  | //  STAGE 2: Generate connected components list | 
|  | //  =========================================== | 
|  | // | 
|  |  | 
|  | // iterate over all actions (start where background action | 
|  | // search left off) | 
|  | for( ; | 
|  | pCurrAct; | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum ) | 
|  | { | 
|  | // execute action to get correct MapModes etc. | 
|  | pCurrAct->Execute( &aMapModeVDev ); | 
|  |  | 
|  | // cache bounds of current action | 
|  | const Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, aMapModeVDev) ); | 
|  |  | 
|  | // accumulate collected bounds here, initialize with current action | 
|  | Rectangle								aTotalBounds( aBBCurrAct ); // thus, | 
|  | // aTotalComponents.aBounds | 
|  | // is | 
|  | // empty | 
|  | // for | 
|  | // non-output-generating | 
|  | // actions | 
|  | bool									bTreatSpecial( false ); | 
|  | ConnectedComponents						aTotalComponents; | 
|  |  | 
|  | // | 
|  | //  STAGE 2.1: Search for intersecting cc entries | 
|  | //  ============================================= | 
|  | // | 
|  |  | 
|  | // if aBBCurrAct is empty, it will intersect with no | 
|  | // aCCList member. Thus, we can save the check. | 
|  | // Furthermore, this ensures that non-output-generating | 
|  | // actions get their own aCCList entry, which is necessary | 
|  | // when copying them to the output metafile (see stage 4 | 
|  | // below). | 
|  |  | 
|  | // #107169# Wholly transparent objects need | 
|  | // not be considered for connected components, | 
|  | // too. Just put each of them into a separate | 
|  | // component. | 
|  | aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, aMapModeVDev); | 
|  |  | 
|  | if( !aBBCurrAct.IsEmpty() && | 
|  | !aTotalComponents.bIsFullyTransparent ) | 
|  | { | 
|  | if( !aBackgroundComponent.aComponentList.empty() && | 
|  | !aBackgroundComponent.aBounds.IsInside(aTotalBounds) ) | 
|  | { | 
|  | // it seems the background is not large enough. to | 
|  | // be on the safe side, combine with this component. | 
|  | aTotalBounds.Union( aBackgroundComponent.aBounds ); | 
|  |  | 
|  | // extract all aCurr actions to aTotalComponents | 
|  | aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(), | 
|  | aBackgroundComponent.aComponentList ); | 
|  |  | 
|  | if( aBackgroundComponent.bIsSpecial ) | 
|  | bTreatSpecial = true; | 
|  | } | 
|  |  | 
|  | ConnectedComponentsList::iterator 		aCurrCC; | 
|  | const ConnectedComponentsList::iterator aLastCC( aCCList.end() ); | 
|  | bool									bSomeComponentsChanged; | 
|  |  | 
|  | // now, this is unfortunate: since changing anyone of | 
|  | // the aCCList elements (e.g. by merging or addition | 
|  | // of an action) might generate new intersection with | 
|  | // other aCCList elements, have to repeat the whole | 
|  | // element scanning, until nothing changes anymore. | 
|  | // Thus, this loop here makes us O(n^3) in the worst | 
|  | // case. | 
|  | do | 
|  | { | 
|  | // only loop here if 'intersects' branch below was hit | 
|  | bSomeComponentsChanged = false; | 
|  |  | 
|  | // iterate over all current members of aCCList | 
|  | for( aCurrCC=aCCList.begin(); aCurrCC != aLastCC; ) | 
|  | { | 
|  | // first check if current element's bounds are | 
|  | // empty. This ensures that empty actions are not | 
|  | // merged into one component, as a matter of fact, | 
|  | // they have no position. | 
|  |  | 
|  | // #107169# Wholly transparent objects need | 
|  | // not be considered for connected components, | 
|  | // too. Just put each of them into a separate | 
|  | // component. | 
|  | if( !aCurrCC->aBounds.IsEmpty() && | 
|  | !aCurrCC->bIsFullyTransparent && | 
|  | aCurrCC->aBounds.IsOver( aTotalBounds ) ) | 
|  | { | 
|  | // union the intersecting aCCList element into aTotalComponents | 
|  |  | 
|  | // calc union bounding box | 
|  | aTotalBounds.Union( aCurrCC->aBounds ); | 
|  |  | 
|  | // extract all aCurr actions to aTotalComponents | 
|  | aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(), | 
|  | aCurrCC->aComponentList ); | 
|  |  | 
|  | if( aCurrCC->bIsSpecial ) | 
|  | bTreatSpecial = true; | 
|  |  | 
|  | // remove and delete aCurrCC element from list (we've now merged its content) | 
|  | aCurrCC = aCCList.erase( aCurrCC ); | 
|  |  | 
|  | // at least one component changed, need to rescan everything | 
|  | bSomeComponentsChanged = true; | 
|  | } | 
|  | else | 
|  | { | 
|  | ++aCurrCC; | 
|  | } | 
|  | } | 
|  | } | 
|  | while( bSomeComponentsChanged ); | 
|  | } | 
|  |  | 
|  | // | 
|  | //  STAGE 2.2: Determine special state for cc element | 
|  | //  ================================================= | 
|  | // | 
|  |  | 
|  | // now test whether the whole connected component must be | 
|  | // treated specially (i.e. rendered as a bitmap): if the | 
|  | // added action is the very first action, or all actions | 
|  | // before it are completely transparent, the connected | 
|  | // component need not be treated specially, not even if | 
|  | // the added action contains transparency. This is because | 
|  | // painting of transparent objects on _white background_ | 
|  | // works without alpha compositing (you just calculate the | 
|  | // color). Note that for the test "all objects before me | 
|  | // are transparent" no sorting is necessary, since the | 
|  | // added metaaction pCurrAct is always in the order the | 
|  | // metafile is painted. Generally, the order of the | 
|  | // metaactions in the ConnectedComponents are not | 
|  | // guaranteed to be the same as in the metafile. | 
|  | if( bTreatSpecial ) | 
|  | { | 
|  | // prev component(s) special -> this one, too | 
|  | aTotalComponents.bIsSpecial = true; | 
|  | } | 
|  | else if( !ImplIsActionSpecial( *pCurrAct ) ) | 
|  | { | 
|  | // added action and none of prev components special -> | 
|  | // this one normal, too | 
|  | aTotalComponents.bIsSpecial = false; | 
|  | } | 
|  | else | 
|  | { | 
|  | // added action is special and none of prev components | 
|  | // special -> do the detailed tests | 
|  |  | 
|  | // can the action handle transparency correctly | 
|  | // (i.e. when painted on white background, does the | 
|  | // action still look correct)? | 
|  | if( !ImplIsActionHandlingTransparency( *pCurrAct ) ) | 
|  | { | 
|  | // no, action cannot handle its transparency on | 
|  | // a printer device, render to bitmap | 
|  | aTotalComponents.bIsSpecial = true; | 
|  | } | 
|  | else | 
|  | { | 
|  | // yes, action can handle its transparency, so | 
|  | // check whether we're on white background | 
|  | if( aTotalComponents.aComponentList.empty() ) | 
|  | { | 
|  | // nothing between pCurrAct and page | 
|  | // background -> don't be special | 
|  | aTotalComponents.bIsSpecial = false; | 
|  | } | 
|  | else | 
|  | { | 
|  | // #107169# Fixes abnove now ensure that _no_ | 
|  | // object in the list is fully transparent. Thus, | 
|  | // if the component list is not empty above, we | 
|  | // must assume that we have to treat this | 
|  | // component special. | 
|  |  | 
|  | // there are non-transparent objects between | 
|  | // pCurrAct and the empty sheet of paper -> be | 
|  | // special, then | 
|  | aTotalComponents.bIsSpecial = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // | 
|  | //  STAGE 2.3: Add newly generated CC list element | 
|  | //  ============================================== | 
|  | // | 
|  |  | 
|  | // set new bounds and add action to list | 
|  | aTotalComponents.aBounds = aTotalBounds; | 
|  | aTotalComponents.aComponentList.push_back( | 
|  | ::std::make_pair( | 
|  | pCurrAct, nActionNum) ); | 
|  |  | 
|  | // add aTotalComponents as a new entry to aCCList | 
|  | aCCList.push_back( aTotalComponents ); | 
|  |  | 
|  | DBG_ASSERT( !aTotalComponents.aComponentList.empty(), | 
|  | "Printer::GetPreparedMetaFile empty component" ); | 
|  | DBG_ASSERT( !aTotalComponents.aBounds.IsEmpty() || | 
|  | (aTotalComponents.aBounds.IsEmpty() && aTotalComponents.aComponentList.size() == 1), | 
|  | "Printer::GetPreparedMetaFile non-output generating actions must be solitary"); | 
|  | DBG_ASSERT( !aTotalComponents.bIsFullyTransparent || | 
|  | (aTotalComponents.bIsFullyTransparent && aTotalComponents.aComponentList.size() == 1), | 
|  | "Printer::GetPreparedMetaFile fully transparent actions must be solitary"); | 
|  | } | 
|  |  | 
|  | // well now, we've got the list of disjunct connected | 
|  | // components. Now we've got to create a map, which contains | 
|  | // the corresponding aCCList element for every | 
|  | // metaaction. Later on, we always process the complete | 
|  | // metafile for each bitmap to be generated, but switch on | 
|  | // output only for actions contained in the then current | 
|  | // aCCList element. This ensures correct mapmode and attribute | 
|  | // settings for all cases. | 
|  |  | 
|  | // maps mtf actions to CC list entries | 
|  | ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionCount() ); | 
|  |  | 
|  | // iterate over all aCCList members and their contained metaactions | 
|  | ConnectedComponentsList::iterator 		aCurr( aCCList.begin() ); | 
|  | const ConnectedComponentsList::iterator aLast( aCCList.end() ); | 
|  | for( ; aCurr != aLast; ++aCurr ) | 
|  | { | 
|  | ComponentList::iterator		  aCurrentAction( aCurr->aComponentList.begin() ); | 
|  | const ComponentList::iterator aLastAction( aCurr->aComponentList.end() ); | 
|  | for( ; aCurrentAction != aLastAction; ++aCurrentAction ) | 
|  | { | 
|  | // set pointer to aCCList element for corresponding index | 
|  | aCCList_MemberMap[ aCurrentAction->second ] = &(*aCurr); | 
|  | } | 
|  | } | 
|  |  | 
|  | // | 
|  | //  STAGE 3.1: Output background mtf actions (if there are any) | 
|  | //  =========================================================== | 
|  | // | 
|  |  | 
|  | ComponentList::iterator		  aCurrAct( aBackgroundComponent.aComponentList.begin() ); | 
|  | const ComponentList::iterator aLastAct( aBackgroundComponent.aComponentList.end() ); | 
|  | for( ; aCurrAct != aLastAct; ++aCurrAct ) | 
|  | { | 
|  | // simply add this action (above, we inserted the actions | 
|  | // starting at index 0 up to and including nLastBgAction) | 
|  | rOutMtf.AddAction( ( aCurrAct->first->Duplicate(), aCurrAct->first ) ); | 
|  | } | 
|  |  | 
|  |  | 
|  | // | 
|  | //  STAGE 3.2: Generate banded bitmaps for special regions | 
|  | //  ==================================================== | 
|  | // | 
|  |  | 
|  | Point aPageOffset; | 
|  | Size aTmpSize( GetOutputSizePixel() ); | 
|  | if( mpPDFWriter ) | 
|  | { | 
|  | aTmpSize = mpPDFWriter->getCurPageSize(); | 
|  | aTmpSize = LogicToPixel( aTmpSize, MapMode( MAP_POINT ) ); | 
|  |  | 
|  | // also add error code to PDFWriter | 
|  | mpPDFWriter->insertError( vcl::PDFWriter::Warning_Transparency_Converted ); | 
|  | } | 
|  | else if( meOutDevType == OUTDEV_PRINTER ) | 
|  | { | 
|  | Printer* pThis = dynamic_cast<Printer*>(this); | 
|  | aPageOffset = pThis->GetPageOffsetPixel(); | 
|  | aPageOffset = Point( 0, 0 ) - aPageOffset; | 
|  | aTmpSize  = pThis->GetPaperSizePixel(); | 
|  | } | 
|  | const Rectangle aOutputRect( aPageOffset, aTmpSize ); | 
|  | bool bTiling = dynamic_cast<Printer*>(this) != NULL; | 
|  |  | 
|  | // iterate over all aCCList members and generate bitmaps for the special ones | 
|  | for( aCurr = aCCList.begin(); aCurr != aLast; ++aCurr ) | 
|  | { | 
|  | if( aCurr->bIsSpecial ) | 
|  | { | 
|  | Rectangle aBoundRect( aCurr->aBounds ); | 
|  | aBoundRect.Intersection( aOutputRect ); | 
|  |  | 
|  | const double fBmpArea( (double) aBoundRect.GetWidth() * aBoundRect.GetHeight() ); | 
|  | const double fOutArea( (double) aOutputRect.GetWidth() * aOutputRect.GetHeight() ); | 
|  |  | 
|  | // check if output doesn't exceed given size | 
|  | if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( 0.25 * fOutArea ) ) ) | 
|  | { | 
|  | // output normally. Therefore, we simply clear the | 
|  | // special attribute, as everything non-special is | 
|  | // copied to rOutMtf further below. | 
|  | aCurr->bIsSpecial = false; | 
|  | } | 
|  | else | 
|  | { | 
|  | // create new bitmap action first | 
|  | if( aBoundRect.GetWidth() && aBoundRect.GetHeight() ) | 
|  | { | 
|  | Point           aDstPtPix( aBoundRect.TopLeft() ); | 
|  | Size			aDstSzPix; | 
|  |  | 
|  | VirtualDevice	aMapVDev;	// here, we record only mapmode information | 
|  | aMapVDev.EnableOutput(sal_False); | 
|  |  | 
|  | VirtualDevice 	aPaintVDev; // into this one, we render. | 
|  | aPaintVDev.SetBackground( aBackgroundComponent.aBgColor ); | 
|  |  | 
|  | rOutMtf.AddAction( new MetaPushAction( PUSH_MAPMODE ) ); | 
|  | rOutMtf.AddAction( new MetaMapModeAction() ); | 
|  |  | 
|  | aPaintVDev.SetDrawMode( GetDrawMode() ); | 
|  |  | 
|  | while( aDstPtPix.Y() <= aBoundRect.Bottom() ) | 
|  | { | 
|  | aDstPtPix.X() = aBoundRect.Left(); | 
|  | aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize(); | 
|  |  | 
|  | if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1L ) > aBoundRect.Bottom() ) | 
|  | aDstSzPix.Height() = aBoundRect.Bottom() - aDstPtPix.Y() + 1L; | 
|  |  | 
|  | while( aDstPtPix.X() <= aBoundRect.Right() ) | 
|  | { | 
|  | if( ( aDstPtPix.X() + aDstSzPix.Width() - 1L ) > aBoundRect.Right() ) | 
|  | aDstSzPix.Width() = aBoundRect.Right() - aDstPtPix.X() + 1L; | 
|  |  | 
|  | if( !Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() && | 
|  | aPaintVDev.SetOutputSizePixel( aDstSzPix ) ) | 
|  | { | 
|  | aPaintVDev.Push(); | 
|  | aMapVDev.Push(); | 
|  |  | 
|  | aMapVDev.mnDPIX = aPaintVDev.mnDPIX = mnDPIX; | 
|  | aMapVDev.mnDPIY = aPaintVDev.mnDPIY = mnDPIY; | 
|  |  | 
|  | aPaintVDev.EnableOutput(sal_False); | 
|  |  | 
|  | // iterate over all actions | 
|  | for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0; | 
|  | pCurrAct; | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum ) | 
|  | { | 
|  | // enable output only for | 
|  | // actions that are members of | 
|  | // the current aCCList element | 
|  | // (aCurr) | 
|  | if( aCCList_MemberMap[nActionNum] == &(*aCurr) ) | 
|  | aPaintVDev.EnableOutput(sal_True); | 
|  |  | 
|  | // but process every action | 
|  | const sal_uInt16 nType( pCurrAct->GetType() ); | 
|  |  | 
|  | if( META_MAPMODE_ACTION == nType ) | 
|  | { | 
|  | pCurrAct->Execute( &aMapVDev ); | 
|  |  | 
|  | MapMode 	aMtfMap( aMapVDev.GetMapMode() ); | 
|  | const Point aNewOrg( aMapVDev.PixelToLogic( aDstPtPix ) ); | 
|  |  | 
|  | aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) ); | 
|  | aPaintVDev.SetMapMode( aMtfMap ); | 
|  | } | 
|  | else if( ( META_PUSH_ACTION == nType ) || ( META_POP_ACTION ) == nType ) | 
|  | { | 
|  | pCurrAct->Execute( &aMapVDev ); | 
|  | pCurrAct->Execute( &aPaintVDev ); | 
|  | } | 
|  | else if( META_GRADIENT_ACTION == nType ) | 
|  | { | 
|  | MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct); | 
|  | Printer* pPrinter = dynamic_cast< Printer* >(this); | 
|  | if( pPrinter ) | 
|  | pPrinter->DrawGradientEx( &aPaintVDev, pGradientAction->GetRect(), pGradientAction->GetGradient() ); | 
|  | else | 
|  | DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() ); | 
|  | } | 
|  | else | 
|  | { | 
|  | pCurrAct->Execute( &aPaintVDev ); | 
|  | } | 
|  |  | 
|  | if( !( nActionNum % 8 ) ) | 
|  | Application::Reschedule(); | 
|  | } | 
|  |  | 
|  | const sal_Bool bOldMap = mbMap; | 
|  | mbMap = aPaintVDev.mbMap = sal_False; | 
|  |  | 
|  | Bitmap aBandBmp( aPaintVDev.GetBitmap( Point(), aDstSzPix ) ); | 
|  |  | 
|  | // scale down bitmap, if requested | 
|  | if( bDownsampleBitmaps ) | 
|  | { | 
|  | aBandBmp = GetDownsampledBitmap( aDstSzPix, | 
|  | Point(), aBandBmp.GetSizePixel(), | 
|  | aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY ); | 
|  | } | 
|  |  | 
|  | rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) ); | 
|  | rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) ); | 
|  | rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) ); | 
|  |  | 
|  | aPaintVDev.mbMap = sal_True; | 
|  | mbMap = bOldMap; | 
|  | aMapVDev.Pop(); | 
|  | aPaintVDev.Pop(); | 
|  | } | 
|  |  | 
|  | // overlapping bands to avoid missing lines (e.g. PostScript) | 
|  | aDstPtPix.X() += aDstSzPix.Width(); | 
|  | } | 
|  |  | 
|  | // overlapping bands to avoid missing lines (e.g. PostScript) | 
|  | aDstPtPix.Y() += aDstSzPix.Height(); | 
|  | } | 
|  |  | 
|  | rOutMtf.AddAction( new MetaPopAction() ); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // clean up aMapModeVDev | 
|  | nCount = aMapModeVDev.GetGCStackDepth(); | 
|  | while( nCount-- ) | 
|  | aMapModeVDev.Pop(); | 
|  |  | 
|  | // | 
|  | //  STAGE 4: Copy actions to output metafile | 
|  | //  ======================================== | 
|  | // | 
|  |  | 
|  | // iterate over all actions and duplicate the ones not in a | 
|  | // special aCCList member into rOutMtf | 
|  | for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0; | 
|  | pCurrAct; | 
|  | pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum ) | 
|  | { | 
|  | const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum]; | 
|  |  | 
|  | // NOTE: This relies on the fact that map-mode or draw | 
|  | // mode changing actions are solitary aCCList elements and | 
|  | // have empty bounding boxes, see comment on stage 2.1 | 
|  | // above | 
|  | if( pCurrAssociatedComponent && | 
|  | (pCurrAssociatedComponent->aBounds.IsEmpty() || | 
|  | !pCurrAssociatedComponent->bIsSpecial) ) | 
|  | { | 
|  | // #107169# Treat transparent bitmaps special, if they | 
|  | // are the first (or sole) action in their bounds | 
|  | // list. Note that we previously ensured that no | 
|  | // fully-transparent objects are before us here. | 
|  | if( ImplIsActionHandlingTransparency( *pCurrAct ) && | 
|  | pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct ) | 
|  | { | 
|  | // convert actions, where masked-out parts are of | 
|  | // given background color | 
|  | ImplConvertTransparentAction(rOutMtf, | 
|  | *pCurrAct, | 
|  | aMapModeVDev, | 
|  | aBackgroundComponent.aBgColor); | 
|  | } | 
|  | else | 
|  | { | 
|  | // simply add this action | 
|  | rOutMtf.AddAction( ( pCurrAct->Duplicate(), pCurrAct ) ); | 
|  | } | 
|  |  | 
|  | pCurrAct->Execute(&aMapModeVDev); | 
|  | } | 
|  | } | 
|  |  | 
|  | rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() ); | 
|  | rOutMtf.SetPrefSize( rInMtf.GetPrefSize() ); | 
|  | } | 
|  | return bTransparent; | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | Bitmap OutputDevice::GetDownsampledBitmap( const Size& rDstSz, | 
|  | const Point& rSrcPt, const Size& rSrcSz, | 
|  | const Bitmap& rBmp, long nMaxBmpDPIX, long nMaxBmpDPIY ) | 
|  | { | 
|  | Bitmap aBmp( rBmp ); | 
|  |  | 
|  | if( !aBmp.IsEmpty() ) | 
|  | { | 
|  | Point           aPoint; | 
|  | const Rectangle aBmpRect( aPoint, aBmp.GetSizePixel() ); | 
|  | Rectangle       aSrcRect( rSrcPt, rSrcSz ); | 
|  |  | 
|  | // do cropping if neccessary | 
|  | if( aSrcRect.Intersection( aBmpRect ) != aBmpRect ) | 
|  | { | 
|  | if( !aSrcRect.IsEmpty() ) | 
|  | aBmp.Crop( aSrcRect ); | 
|  | else | 
|  | aBmp.SetEmpty(); | 
|  | } | 
|  |  | 
|  | if( !aBmp.IsEmpty() ) | 
|  | { | 
|  | // do downsampling if neccessary | 
|  | Size aDstSizeTwip( PixelToLogic( LogicToPixel( rDstSz ), MAP_TWIP ) ); | 
|  |  | 
|  | // #103209# Normalize size (mirroring has to happen outside of this method) | 
|  | aDstSizeTwip = Size( labs(aDstSizeTwip.Width()), labs(aDstSizeTwip.Height()) ); | 
|  |  | 
|  | const Size      aBmpSize( aBmp.GetSizePixel() ); | 
|  | const double    fBmpPixelX = aBmpSize.Width(); | 
|  | const double    fBmpPixelY = aBmpSize.Height(); | 
|  | const double    fMaxPixelX = aDstSizeTwip.Width() * nMaxBmpDPIX / 1440.0; | 
|  | const double    fMaxPixelY = aDstSizeTwip.Height() * nMaxBmpDPIY / 1440.0; | 
|  |  | 
|  | // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance) | 
|  | if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) || | 
|  | ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) && | 
|  | ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) ) | 
|  | { | 
|  | // do scaling | 
|  | Size            aNewBmpSize; | 
|  | const double    fBmpWH = fBmpPixelX / fBmpPixelY; | 
|  | const double    fMaxWH = fMaxPixelX / fMaxPixelY; | 
|  |  | 
|  | if( fBmpWH < fMaxWH ) | 
|  | { | 
|  | aNewBmpSize.Width() = FRound( fMaxPixelY * fBmpWH ); | 
|  | aNewBmpSize.Height() = FRound( fMaxPixelY ); | 
|  | } | 
|  | else if( fBmpWH > 0.0 ) | 
|  | { | 
|  | aNewBmpSize.Width() = FRound( fMaxPixelX ); | 
|  | aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH); | 
|  | } | 
|  |  | 
|  | if( aNewBmpSize.Width() && aNewBmpSize.Height() ) | 
|  | aBmp.Scale( aNewBmpSize ); | 
|  | else | 
|  | aBmp.SetEmpty(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return aBmp; | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | BitmapEx OutputDevice::GetDownsampledBitmapEx( const Size& rDstSz, | 
|  | const Point& rSrcPt, const Size& rSrcSz, | 
|  | const BitmapEx& rBmpEx, long nMaxBmpDPIX, long nMaxBmpDPIY ) | 
|  | { | 
|  | BitmapEx aBmpEx( rBmpEx ); | 
|  |  | 
|  | if( !aBmpEx.IsEmpty() ) | 
|  | { | 
|  | Point           aPoint; | 
|  | const Rectangle aBmpRect( aPoint, aBmpEx.GetSizePixel() ); | 
|  | Rectangle       aSrcRect( rSrcPt, rSrcSz ); | 
|  |  | 
|  | // do cropping if neccessary | 
|  | if( aSrcRect.Intersection( aBmpRect ) != aBmpRect ) | 
|  | { | 
|  | if( !aSrcRect.IsEmpty() ) | 
|  | aBmpEx.Crop( aSrcRect ); | 
|  | else | 
|  | aBmpEx.SetEmpty(); | 
|  | } | 
|  |  | 
|  | if( !aBmpEx.IsEmpty() ) | 
|  | { | 
|  | // do downsampling if neccessary | 
|  | Size aDstSizeTwip( PixelToLogic( LogicToPixel( rDstSz ), MAP_TWIP ) ); | 
|  |  | 
|  | // #103209# Normalize size (mirroring has to happen outside of this method) | 
|  | aDstSizeTwip = Size( labs(aDstSizeTwip.Width()), labs(aDstSizeTwip.Height()) ); | 
|  |  | 
|  | const Size      aBmpSize( aBmpEx.GetSizePixel() ); | 
|  | const double    fBmpPixelX = aBmpSize.Width(); | 
|  | const double    fBmpPixelY = aBmpSize.Height(); | 
|  | const double    fMaxPixelX = aDstSizeTwip.Width() * nMaxBmpDPIX / 1440.0; | 
|  | const double    fMaxPixelY = aDstSizeTwip.Height() * nMaxBmpDPIY / 1440.0; | 
|  |  | 
|  | // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance) | 
|  | if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) || | 
|  | ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) && | 
|  | ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) ) | 
|  | { | 
|  | // do scaling | 
|  | Size            aNewBmpSize; | 
|  | const double    fBmpWH = fBmpPixelX / fBmpPixelY; | 
|  | const double    fMaxWH = fMaxPixelX / fMaxPixelY; | 
|  |  | 
|  | if( fBmpWH < fMaxWH ) | 
|  | { | 
|  | aNewBmpSize.Width() = FRound( fMaxPixelY * fBmpWH ); | 
|  | aNewBmpSize.Height() = FRound( fMaxPixelY ); | 
|  | } | 
|  | else if( fBmpWH > 0.0 ) | 
|  | { | 
|  | aNewBmpSize.Width() = FRound( fMaxPixelX ); | 
|  | aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH); | 
|  | } | 
|  |  | 
|  | if( aNewBmpSize.Width() && aNewBmpSize.Height() ) | 
|  | aBmpEx.Scale( aNewBmpSize ); | 
|  | else | 
|  | aBmpEx.SetEmpty(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return aBmpEx; | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | void Printer::DrawGradientEx( OutputDevice* pOut, const Rectangle& rRect, const Gradient& rGradient ) | 
|  | { | 
|  | const PrinterOptions& rPrinterOptions = GetPrinterOptions(); | 
|  |  | 
|  | if( rPrinterOptions.IsReduceGradients() ) | 
|  | { | 
|  | if( PRINTER_GRADIENT_STRIPES == rPrinterOptions.GetReducedGradientMode() ) | 
|  | { | 
|  | if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) ) | 
|  | { | 
|  | Gradient aNewGradient( rGradient ); | 
|  |  | 
|  | aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() ); | 
|  | pOut->DrawGradient( rRect, aNewGradient ); | 
|  | } | 
|  | else | 
|  | pOut->DrawGradient( rRect, rGradient ); | 
|  | } | 
|  | else | 
|  | { | 
|  | const Color&    rStartColor = rGradient.GetStartColor(); | 
|  | const Color&    rEndColor = rGradient.GetEndColor(); | 
|  | const long      nR = ( ( (long) rStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100L + | 
|  | ( (long) rEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100L ) >> 1; | 
|  | const long      nG = ( ( (long) rStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100L + | 
|  | ( (long) rEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100L ) >> 1; | 
|  | const long      nB = ( ( (long) rStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100L + | 
|  | ( (long) rEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100L ) >> 1; | 
|  | const Color     aColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ); | 
|  |  | 
|  | pOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); | 
|  | pOut->SetLineColor( aColor ); | 
|  | pOut->SetFillColor( aColor ); | 
|  | pOut->DrawRect( rRect ); | 
|  | pOut->Pop(); | 
|  | } | 
|  | } | 
|  | else | 
|  | pOut->DrawGradient( rRect, rGradient ); | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | void Printer::DrawGradientEx( OutputDevice* pOut, const PolyPolygon& rPolyPoly, const Gradient& rGradient ) | 
|  | { | 
|  | const PrinterOptions& rPrinterOptions = GetPrinterOptions(); | 
|  |  | 
|  | if( rPrinterOptions.IsReduceGradients() ) | 
|  | { | 
|  | if( PRINTER_GRADIENT_STRIPES == rPrinterOptions.GetReducedGradientMode() ) | 
|  | { | 
|  | if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) ) | 
|  | { | 
|  | Gradient aNewGradient( rGradient ); | 
|  |  | 
|  | aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() ); | 
|  | pOut->DrawGradient( rPolyPoly, aNewGradient ); | 
|  | } | 
|  | else | 
|  | pOut->DrawGradient( rPolyPoly, rGradient ); | 
|  | } | 
|  | else | 
|  | { | 
|  | const Color&    rStartColor = rGradient.GetStartColor(); | 
|  | const Color&    rEndColor = rGradient.GetEndColor(); | 
|  | const long      nR = ( ( (long) rStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100L + | 
|  | ( (long) rEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100L ) >> 1; | 
|  | const long      nG = ( ( (long) rStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100L + | 
|  | ( (long) rEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100L ) >> 1; | 
|  | const long      nB = ( ( (long) rStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100L + | 
|  | ( (long) rEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100L ) >> 1; | 
|  | const Color     aColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ); | 
|  |  | 
|  | pOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); | 
|  | pOut->SetLineColor( aColor ); | 
|  | pOut->SetFillColor( aColor ); | 
|  | pOut->DrawPolyPolygon( rPolyPoly ); | 
|  | pOut->Pop(); | 
|  | } | 
|  | } | 
|  | else | 
|  | pOut->DrawGradient( rPolyPoly, rGradient ); | 
|  | } |