| /************************************************************** |
| * |
| * 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 ); |
| } |